Compare commits
104 Commits
61878c20ba
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 16ab237551 | |||
| bf8a57359c | |||
| 6c4f9a97a6 | |||
| 266dea2c86 | |||
| 493ba4ccef | |||
| b3752585ca | |||
| 6ab0f8c137 | |||
| 80c7c5e8f8 | |||
| 5adb1e807b | |||
| 0b2425f506 | |||
| 8487ac5ca0 | |||
| 517059d53e | |||
| d1e6529950 | |||
| d296032e90 | |||
| da55c61edd | |||
| f4af2fd137 | |||
| fd66ca9c9b | |||
| a85f9aabd5 | |||
| d812b8b44a | |||
| 3892a459e5 | |||
| 27f125cbe8 | |||
| 0e92966a5d | |||
| 6a399ea7ca | |||
| 5b915bbc22 | |||
| 0409d63874 | |||
| b86f3209f5 | |||
| 895bec2a50 | |||
| 9b1d6ffb5d | |||
| bdc8bd3d93 | |||
| efbb94b43d | |||
| 8dfd48fe52 | |||
| 71b799dbf3 | |||
| 2026230ff6 | |||
| 2e6ed487fd | |||
| acd30dbb13 | |||
| b3831273d6 | |||
| 91282ba908 | |||
| 5e71e8b7c9 | |||
| 433c275f40 | |||
| 96ea3788d5 | |||
| aa25c6dec5 | |||
| 61c50eecc1 | |||
| f425cef139 | |||
| 52a0b7f3c7 | |||
| fac5e2ea5e | |||
| e9d1b733a5 | |||
| 8a5ff01619 | |||
| 39897c5bfe | |||
| c1cdf835af | |||
| 1ec70fcab8 | |||
| 29fbd71d8f | |||
| fad50a1b1b | |||
| 7907ff0def | |||
| 370141c83b | |||
| 36196a882f | |||
| 22ceb8e2b4 | |||
| 2a48d4a4c5 | |||
| 01d72f4885 | |||
| 2d1c1654f9 | |||
| ed5c7ea79d | |||
| b0dc637b8f | |||
| b5791a0871 | |||
| bd3d747ede | |||
| 73d1fd9135 | |||
| 253407abc3 | |||
| 3387769578 | |||
| b0a9b3c12a | |||
| 272d99a2bd | |||
| 3437c6259b | |||
| 6fc9f38182 | |||
| 7c8e812d4b | |||
| 0d114e12c2 | |||
| 26a7315ea1 | |||
| 51d0b8b3fc | |||
| 7038542f14 | |||
| c05fa1eaa5 | |||
| dc470bf74b | |||
| b2fc051cbb | |||
| cdbd9318cf | |||
| 0668ecccf3 | |||
| 789d99aa12 | |||
| ca3dd78783 | |||
| c1472d5363 | |||
| c2a1ca3ee5 | |||
| 1e5f792854 | |||
| 43cd51aa13 | |||
| 2d58658420 | |||
| e837351a6e | |||
| fcfdac87b4 | |||
| 2de30fbde6 | |||
| 22bb446309 | |||
| 168dedcdc5 | |||
| 5e5fe02bb7 | |||
| 226371e0f6 | |||
| b4502027fa | |||
| 926cd5dd06 | |||
| d641036327 | |||
| 8d5dc3a5d1 | |||
| 4913765584 | |||
| 9c3fbbdc4b | |||
| f5562871c0 | |||
| 020bf86404 | |||
| 79f1ee371b | |||
| 0f88a68c59 |
1
dist/assets/index-3J8Zo9Sf.css
vendored
1
dist/assets/index-CfMiYyAp.css
vendored
Normal file
161
dist/assets/index-D__qMdwn.js
vendored
Normal file
60
dist/assets/index-Hdj79d7b.js
vendored
97
dist/assets/popcat-DOGy5LFs.svg
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Слой_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 994.3 1000.5" style="enable-background:new 0 0 994.3 1000.5;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#F7F5F8;}
|
||||
.st1{fill:#DCC4D4;}
|
||||
.st2{fill:#EFAD93;}
|
||||
.st3{fill:#D68C72;}
|
||||
.st4{fill:#964C49;}
|
||||
.st5{fill:#3A0101;}
|
||||
.st6{fill:#894544;}
|
||||
.st7{fill:#E3746D;}
|
||||
.st8{fill:#FFFFFF;}
|
||||
.st9{fill:#623538;}
|
||||
.st10{fill:#C95755;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M27.4,444.9l471.8-254l124.2-2.5c0,0,129.7,15.8,232.7,116.6c0.1,0.1,0.2,0.1,0.2,0.2
|
||||
c28.2,27.7,54.4,62.5,75.1,106.3c1.4,2.9,2.8,5.9,4.1,8.9c0,0,151.2,244-41.3,456.4c-192.5,212.3-651.4,110.6-789.3-112.5
|
||||
c-1-1.5-2-3.1-3-4.7c-21.2-34.1-37-67.9-48.6-99.2c-14.7-39.6-22.9-75.1-27.4-101.6C20.8,528.5,27.5,444.9,27.4,444.9z"/>
|
||||
<path class="st1" d="M218.8,420.3c-31.3,88.9,7.8,189.9,85.9,99.6c111.2-153,321.4-183.3,405.1-172.5
|
||||
c102.5,19.8,182.5,123.4,207.9,125.4c25.4,2,13.7-61.3,13.7-61.3c1.4,2.9,2.8,5.9,4.1,8.9c0,0,151.2,244-41.3,456.4
|
||||
C701.7,1089.1,242.8,987.4,105,764.2c-1-1.5-2-3.1-3-4.7c-21.2-34.1-37-67.9-48.6-99.2C106.8,572.1,243.9,348.5,218.8,420.3z"/>
|
||||
<path class="st2" d="M680.7,183.7c49.4-48.5,129.9-117.2,190.6-152.4c29.2-17,53.8-26.2,68.1-21.7c2.8,0.8,5.1,2.2,7.1,4.1
|
||||
c23.7,23.2,14.4,169.4,3.6,279.9c-7,70.6-14.6,126.7-14.6,126.7c-15.5-33.2-32-65.7-81.8-118c-20.7-20.3-41.8-37.4-63.2-51.7
|
||||
c-36-24-72.5-40.1-108.3-50.3c-4.8-1.4-9.6-2.6-14.4-3.8C671.8,192.5,676.1,188.2,680.7,183.7z"/>
|
||||
<path class="st3" d="M871.3,31.4c-10.1,73.5-12.2,200.9,78.8,262.3c-7,70.6-14.6,126.7-14.6,126.7c-15.5-33.2-32-65.7-81.8-118
|
||||
c-20.7-20.3-41.8-37.4-63.2-51.7c-36-24-72.5-40.1-108.3-50.3l-1.6-16.6C730.1,135.2,810.6,66.6,871.3,31.4z"/>
|
||||
<path class="st4" d="M51.1,14.9c49.7-15.8,213.2,136.5,315.7,216.5c0,0,141.2-74,301.1-34.9c7.5,1.8,15.1,3.9,22.8,6.3
|
||||
c-46.6-1.7-115.4-3.1-144.9,2.2C499,213.5,329,264,254.7,383.2c-74.3,119.1-71.3,258.8-69.5,320.2c1.4,48.9-34.4,79.5-88.3,48
|
||||
c-44.5-74.3-63.2-146.8-71-192.6c-1.1-10.1-2.2-20.5-3.5-31c-0.8-6-1.4-12-2.1-17.9C-12.3,223.4,2.5,30.3,51.1,14.9z"/>
|
||||
<path class="st4" d="M939.4,9.6c-41.2,32.2-128.1,113.5-148.9,241c-40.8-27.2-82.3-44.2-122.7-54.1C736,126.7,892.6-5,939.4,9.6z"
|
||||
/>
|
||||
<path class="st5" d="M667.3,196.7c18-21.7,38.7-41.3,59-60.8c30.9-28.7,63.1-56.1,97.5-80.5c23.1-16.3,47-31.5,73.2-42.8
|
||||
c13.8-5.3,28.2-11.2,43.7-6.9c3.2,0.8,4.1,5.5,1.3,7.4c-48,35.5-89.8,81.2-115,136l-7.3,16.4l-6.3,16.8c-8.3,23.1-13.5,47.2-19,71
|
||||
c-1.4,2.2-4.4,2.7-6.5,1.3c-9.3-6.2-18.7-12.1-28.3-17.7C730.6,220,699.1,207.3,667.3,196.7L667.3,196.7z M668.3,196.4
|
||||
c33.5,5.2,65.5,17.4,95.4,33c10,5.2,19.8,11.1,29.3,17.3l-7.2,3.2c11.1-73.1,51.8-137.4,99-192.5c15.9-18.4,33.2-35.6,52-51.1
|
||||
l1.3,7.4c-34.7-7-111.7,54.4-141.3,75.9C755.4,121.4,714.9,155,676,189.9C673.8,191.8,670.7,194.7,668.3,196.4L668.3,196.4z"/>
|
||||
<path class="st5" d="M667,196.8c9.8-12.7,21.5-23.8,32.5-35.3c56.3-55.7,115.3-109,185.4-147.2C903.6,5.5,934-9.3,952.6,8
|
||||
c8.6,9.3,11,23.4,12.9,34.8c7.6,63.5,0.5,127.1-5.9,190.2c-7.1,62.7-14.3,125.2-20.2,188c-0.1,1.2-0.9,2.5-2.1,3.1
|
||||
c-1.9,1-4.3,0.2-5.3-1.7c-21.3-43.7-51.6-81.8-85.2-116.5c-29-28.5-61.5-53.6-97.1-73.3C723.7,217.7,695.1,206.6,667,196.8
|
||||
L667,196.8z M668.6,196.3c102.9,16.6,200.3,90.3,251,181.1c7.4,13.4,13.7,27.4,19.6,41.3l-7.6,1.2c15.8-93,21.6-187.6,22.5-281.8
|
||||
c0.1-30.9,0.3-62.6-4.6-92.8c-1.5-8.2-3.1-16-6.7-22.5c-2.2-3.9-5.1-5.3-10-5.6c-13.2-0.3-28,6.5-40.3,12.5
|
||||
c-13.6,6.6-26.9,14.5-39.9,22.9c-62,40.5-119.8,87.7-175.4,136.6C674.9,191.3,671.1,194.7,668.6,196.3L668.6,196.3z"/>
|
||||
<path class="st5" d="M368.1,222.4c0,0-27.7,15.2-48.3,39.2c32.8-26.4,66.9-39.2,66.9-39.2H368.1z"/>
|
||||
<path class="st5" d="M51.4,15.6C-13.1,43.9,7.5,316.1,13.6,383c3.6,42.3,8.6,84.5,13.7,126.7c0.9,16.6,3.4,33.8,6.5,50.3
|
||||
c15.8,83.2,49,164.3,101,231.4c162.6,201.4,596.3,285.2,772.8,64.1c93.2-112.3,103.3-255.1,47.5-387.3c-6.8-15.7-14.3-31.3-23-45.7
|
||||
c-9.8-21.3-21.3-41.7-34.7-60.8C858,307,809.5,265.5,746.8,233.1c-117.3-59-260-50.3-377.9,2.6c-1.6,0.8-3.7,0.7-5.2-0.5
|
||||
c-50.3-40.8-98.5-83.9-148.5-125C183.7,85.2,89.5,5.3,51.4,15.6L51.4,15.6z M50.9,14.1c38.2-10.9,133.1,68.2,166.8,93
|
||||
c51.1,39.6,100.6,81.4,152,120.5l-5.2-0.5c10.1-5.3,20.2-9.7,30.6-13.9c113.6-45.9,247.7-50.1,357.9,7.6
|
||||
c38.5,19.3,80,51.8,106.2,79.6c28.3,26.3,63.5,77.6,79.6,117.9c37.5,68.6,57.7,147.2,55.3,225.5c-2,78.7-32.2,155.1-80.9,216.3
|
||||
c-144.1,184.7-435.8,164.2-626.6,69.8c-70.3-34.9-136.1-83.3-181-148.9c-52.9-79.3-87.3-177.1-92-270.3
|
||||
c-12-126.6-19.9-254.6-7-381.4C10.8,98.9,18.7,25.8,50.9,14.1L50.9,14.1z"/>
|
||||
<path class="st2" d="M56.5,68.9c10.8,4.5,48,42.6,88.9,92c33.1,40,68.6,87.5,94.4,130.5C256.3,318.9,134.9,522,113,560.3
|
||||
c-20.1,35.1-64.5-92-76.1-226c-1.1-12-1.8-23.9-2.3-35.9C28.8,152.3,36.9,60.8,56.5,68.9z"/>
|
||||
<path class="st6" d="M327.6,643.2c7.1-85.1,52.7-166.4,117.6-206.6c75-42,139.1-61.4,202.6-61.1l0,0c15.2,0.1,30.5,1.3,45.8,3.6
|
||||
c15,2.3,30.1,5.6,45.5,9.9c66,18.5,208.6,88.2,186.5,282.8c-0.7,5.8-1.5,11.5-2.5,17.2C891,873.9,688.2,995.2,467.4,887.4
|
||||
C358,834,319.8,736.3,327.6,643.2z"/>
|
||||
<path class="st7" d="M636.5,336.4c9.7,2.9,32.2,11.3,56.1-1c23.9-12.3,33.2,15.1,20.5,22.1c-14.4,7.9-18.4,17-19.6,21.7
|
||||
c-15.3-2.3-30.5-3.5-45.8-3.6l0,0c-4.1-7.4-25.4-8.4-33.2-21.1C606.7,341.7,626.8,333.4,636.5,336.4z"/>
|
||||
<path class="st8" d="M386.7,314c6.2-5.8,12.6-10.9,19.1-15.3c58-40,121.3-28.3,130.8-3.7c10.6,27.3-35.8,36.7-64.2,48.6
|
||||
c-25.9,10.8-56.1,30.6-78.7,43.5c-16.6,9.5-29.3,15.3-33.1,11.1C351.5,388.3,342.5,354.9,386.7,314z"/>
|
||||
<path class="st8" d="M773.8,272.5c33.4,12.4,58.6,33.3,75,50.7c11.4,12.1,18.4,22.5,20.9,27.2c6.1,11.4-12.7,14-36.2,5.3
|
||||
c-1-0.4-2.1-0.8-3.2-1.2c-25.3-9-84.3-25.6-101.1-33C705.1,310.9,717.2,251.5,773.8,272.5z"/>
|
||||
<path class="st5" d="M405.8,298.7c58-40,121.3-28.3,130.8-3.7c10.6,27.3-35.8,36.7-64.2,48.6c-25.9,10.8-56.1,30.6-78.7,43.5
|
||||
C370.3,352.9,386.3,320.8,405.8,298.7z"/>
|
||||
<path class="st5" d="M773.8,272.5c33.4,12.4,58.6,33.3,75,50.7c0.6,10.9-2.7,23.7-18.5,31.3c-25.3-9-84.3-25.6-101.1-33
|
||||
C705.1,310.9,717.2,251.5,773.8,272.5z"/>
|
||||
<path class="st5" d="M504.4,274.3c-72.3-19-136.4,35.7-149,74s-0.5,51.9,0,40.6s0-28.9,18-52.9c18.1-24,76-50.5,76-50.5
|
||||
S477.1,292.5,504.4,274.3z"/>
|
||||
<path class="st5" d="M869.5,341.4c-20.4-25.1-54.2-47.2-54.2-47.2s5.1,17,18.1,25.7c13,8.8,37.3,35.8,37.3,35.8
|
||||
S876.6,352.4,869.5,341.4z"/>
|
||||
<path class="st8" d="M504.4,294.2c-8.5,0.9-74.6,10.1-65.2-3.7s34.7-12.3,55.5-10.7C517.1,281.5,512.9,293.4,504.4,294.2z"/>
|
||||
<path class="st8" d="M803.6,314.5c-12.8-6.2-34.7-17.4-31.5-26s27.8,6.4,42.5,14C833.1,312,816.4,320.7,803.6,314.5z"/>
|
||||
<path class="st9" d="M445.2,436.6c75-42,139.1-61.4,202.6-61.1l0,0c15.2,0.1,30.5,1.3,45.8,3.6c15,2.3,30.1,5.6,45.5,9.9
|
||||
c66,18.5,208.6,88.2,186.5,282.7c-0.7,5.8-1.5,11.5-2.5,17.2C859.5,755.1,757.8,798,643,797.9c-139.3,0-259.6-63.2-315.5-154.7
|
||||
C334.6,558.1,380.2,476.8,445.2,436.6z"/>
|
||||
<path class="st5" d="M467,888.1c-4.1-2.1-15-8-19-10.1c-4.3-2.5-13.8-8.5-18.2-11.3c-5.7-3.9-11.5-8.7-17.3-12.6
|
||||
c-12.8-10.7-25.3-22-35.9-35c-51-59.2-67.4-143.5-50-219.2c14.9-66.8,52.5-130.4,110.4-168.2c74.4-43.3,162.1-73,249-60.2
|
||||
c66.6,9.1,131.6,37.9,178.2,87.3c47.5,48.6,68.8,117.8,66.7,185c-0.4,38.6-7.7,77.3-22.3,113.1C871.9,848.2,785.9,914.7,689,928
|
||||
C613.2,939.7,535.3,921.3,467,888.1L467,888.1z M467.7,886.6c103.1,51,224,52.6,322.3-10.2c62.5-40,109.6-104.2,126.4-177.1
|
||||
c12.6-54.7,12.2-114.2-9.7-166.5C878,462.3,811,414,739.3,394.7C633,365.6,539,391.2,446.1,446.3
|
||||
C361.9,503,321.5,614.9,336.4,714.1c5.5,34.6,19.1,68.2,39.4,96.7c3.2,4,7.6,9.9,10.8,13.9c6.6,7.1,14.9,16.4,22.4,22.6l5.2,4.8
|
||||
l5.6,4.3c6.3,5.3,16.2,11.9,23.2,16.3C449.7,877.2,460.6,882.6,467.7,886.6L467.7,886.6z"/>
|
||||
<path class="st10" d="M624.5,349.4c9.9-5,28,14.1,28,14.1C630,361.9,614.5,354.5,624.5,349.4z"/>
|
||||
<path class="st10" d="M709.7,347.4c-8.3-4.5-19.1,16.1-19.1,16.1C701.2,361.2,718,351.9,709.7,347.4z"/>
|
||||
<path class="st3" d="M145.4,160.9c33.1,40,68.6,87.5,94.4,130.5c16.5,27.5-104.9,230.6-126.9,268.9c-20.1,35.1-64.5-92-76.1-226
|
||||
C134.4,390.3,148.1,261.2,145.4,160.9z"/>
|
||||
<path class="st5" d="M239.3,291.7C203,231.8,156.6,179,109.9,127.3C94.3,110.6,78.5,93.1,61,78.7c-2.4-1.8-4.8-3.7-6.8-4.5
|
||||
c-2.9,2.5-5.2,9.1-6.4,13.8c-7.9,33.4-8.7,68.6-9.8,103.1c-0.4,23.2-0.5,46.6-0.3,69.9c-0.2,70,5.5,140.1,23.6,207.9
|
||||
c5.3,20,24.9,86.9,43.7,93.3c1.6-0.1,2.6-1.2,3.6-2.7l0.9-1.5c3.4-7.1,73.1-128.3,78.8-138.8C201.1,395.1,248.5,315.1,239.3,291.7
|
||||
L239.3,291.7z M240.3,291.1c10.1,18.9-35.6,108.8-46,131.3c-23,47.3-49.2,93.1-76.6,137.9c-2,4.1-7.1,10.6-13.3,9.8
|
||||
c-30.8-4.3-55.4-138.4-61-168.9C30.8,332,27.6,261.4,27.8,191.1c0.8-28.9-0.2-103.5,17.4-124.3c7.7-8.1,16.3-2.5,22.8,3
|
||||
c9.4,7.7,17.6,16.1,25.8,24.6C150,154,197.5,221.4,240.3,291.1L240.3,291.1z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.6 KiB |
52
dist/assets/uni-C5oaqT41.svg
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 400 432.7" style="enable-background:new 0 0 400 432.7;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#F50DB4;}
|
||||
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#F50DB4;}
|
||||
</style>
|
||||
<path class="st0" d="M325.1,65.4c0.6-10.2,2-17,4.8-23.1c1.1-2.4,2.1-4.4,2.3-4.4s-0.3,1.8-1.1,4c-2,6-2.4,14.2-1,23.7
|
||||
c1.8,12.1,2.8,13.8,15.6,26.9c6,6.1,13,13.8,15.5,17.1l4.6,6l-4.6-4.3c-5.6-5.3-18.6-15.5-21.4-17c-1.9-1-2.2-1-3.4,0.2
|
||||
c-1.1,1.1-1.3,2.7-1.5,10.4c-0.2,12-1.9,19.7-5.8,27.3c-2.1,4.2-2.5,3.3-0.5-1.4c1.4-3.5,1.6-5,1.6-16.6c0-23.3-2.8-28.9-19.1-38.5
|
||||
c-4.1-2.4-10.9-5.9-15.1-7.8c-4.2-1.9-7.5-3.5-7.4-3.6c0.5-0.5,16.3,4.2,22.7,6.6c9.5,3.6,11.1,4.1,12.2,3.7
|
||||
C324.4,74.2,324.8,72,325.1,65.4z"/>
|
||||
<path class="st0" d="M124.4,31.5c-5.6-0.9-5.9-1-3.2-1.4c5.1-0.8,17.1,0.3,25.4,2.2c19.3,4.6,36.9,16.3,55.6,37l5,5.5l7.1-1.1
|
||||
c30-4.8,60.6-1,86.1,10.8c7,3.2,18.1,9.7,19.5,11.3c0.4,0.5,1.3,3.9,1.8,7.5c1.9,12.5,0.9,22.1-2.9,29.3c-2.1,3.9-2.2,5.1-0.8,8.5
|
||||
c1.1,2.7,4.3,4.6,7.4,4.6c6.3,0,13.2-10.2,16.3-24.4l1.3-5.6l2.5,2.8c13.7,15.4,24.4,36.4,26.2,51.3l0.5,3.9l-2.3-3.5
|
||||
c-4-6.1-7.9-10.2-13-13.6c-9.2-6-18.9-8.1-44.5-9.4c-23.2-1.2-36.3-3.2-49.3-7.4c-22.1-7.2-33.3-16.7-59.6-51.1
|
||||
c-11.7-15.2-18.9-23.7-26.1-30.5C161,42.8,145,34.7,124.4,31.5z"/>
|
||||
<path class="st0" d="M135.4,105.3c-11.4-15.7-18.5-39.7-17-57.6l0.5-5.6l2.6,0.5c4.9,0.9,13.3,4,17.3,6.4
|
||||
c10.8,6.5,15.5,15.2,20.3,37.3c1.4,6.5,3.2,13.8,4.1,16.3c1.4,4,6.5,13.3,10.7,19.4c3,4.4,1,6.4-5.6,5.8
|
||||
C158,126.9,144.2,117.5,135.4,105.3z"/>
|
||||
<path class="st0" d="M311.3,221.9c-53.5-21.4-72.3-40-72.3-71.4c0-4.6,0.2-8.4,0.4-8.4c0.2,0,2.3,1.5,4.6,3.4
|
||||
c10.8,8.6,23,12.3,56.6,17.2c19.8,2.9,30.9,5.2,41.2,8.6c32.6,10.8,52.8,32.6,57.6,62.4c1.4,8.6,0.6,24.9-1.7,33.4
|
||||
c-1.8,6.7-7.3,18.9-8.7,19.4c-0.4,0.1-0.8-1.4-0.9-3.5c-0.6-11.2-6.2-22-15.8-30.2C361.4,243.6,346.9,236.2,311.3,221.9z"/>
|
||||
<path class="st0" d="M273.7,230.8c-0.7-4-1.8-9-2.6-11.3l-1.4-4l2.5,2.8c3.5,3.9,6.3,8.9,8.6,15.6c1.8,5.1,2,6.6,2,14.9
|
||||
c0,8.1-0.2,9.8-1.9,14.4c-2.6,7.2-5.8,12.4-11.3,17.9c-9.8,9.9-22.3,15.4-40.4,17.6c-3.1,0.4-12.3,1.1-20.4,1.5
|
||||
c-20.3,1.1-33.7,3.2-45.8,7.4c-1.7,0.6-3.3,1-3.4,0.8c-0.5-0.5,7.7-5.3,14.5-8.6c9.5-4.6,19-7.1,40.3-10.6
|
||||
c10.5-1.7,21.4-3.9,24.1-4.7C264.6,276.7,278,256.2,273.7,230.8z"/>
|
||||
<path class="st0" d="M298.2,274.1c-7.1-15.2-8.7-29.9-4.8-43.5c0.4-1.5,1.1-2.7,1.5-2.7c0.4,0,2.1,0.9,3.7,2
|
||||
c3.3,2.2,9.8,5.9,27.3,15.4c21.8,11.8,34.3,21,42.7,31.5c7.4,9.2,12,19.6,14.2,32.3c1.3,7.2,0.5,24.6-1.3,31.8
|
||||
c-5.9,22.9-19.5,40.9-39,51.4c-2.9,1.5-5.4,2.8-5.7,2.8c-0.3,0,0.8-2.6,2.3-5.8c6.5-13.6,7.3-26.8,2.3-41.6c-3-9-9.2-20-21.7-38.6
|
||||
C305.4,287.5,301.8,281.7,298.2,274.1z"/>
|
||||
<path class="st0" d="M97.4,356.1c19.8-16.7,44.5-28.5,67-32.1c9.7-1.6,25.8-0.9,34.8,1.3c14.4,3.7,27.3,11.9,33.9,21.6
|
||||
c6.5,9.5,9.3,17.9,12.3,36.4c1.2,7.3,2.4,14.6,2.8,16.3c2.2,9.6,6.5,17.3,11.8,21.1c8.4,6.1,22.9,6.5,37.1,1
|
||||
c2.4-0.9,4.5-1.6,4.7-1.4c0.5,0.5-6.7,5.3-11.7,7.8c-6.8,3.4-12.2,4.7-19.4,4.7c-13,0-23.9-6.6-32.9-20c-1.8-2.6-5.8-10.6-8.9-17.6
|
||||
c-9.5-21.6-14.2-28.2-25.3-35.4c-9.6-6.3-22.1-7.4-31.4-2.8c-12.3,6-15.7,21.6-6.9,31.5c3.5,3.9,10,7.3,15.3,8
|
||||
c10,1.2,18.5-6.3,18.5-16.3c0-6.5-2.5-10.2-8.8-13.1c-8.6-3.9-17.9,0.7-17.9,8.7c0,3.4,1.5,5.6,5,7.2c2.2,1,2.3,1.1,0.5,0.7
|
||||
c-7.9-1.6-9.8-11.1-3.4-17.4c7.7-7.6,23.5-4.2,28.9,6.1c2.3,4.3,2.5,13,0.6,18.2c-4.5,11.7-17.4,17.8-30.6,14.5
|
||||
c-9-2.3-12.6-4.7-23.4-15.8c-18.8-19.3-26.1-23-53.2-27.2l-5.2-0.8L97.4,356.1z"/>
|
||||
<path class="st1" d="M9.2,11.5c62.8,75.7,106,107,110.8,113.6c4,5.5,2.5,10.4-4.3,14.2c-3.8,2.1-11.5,4.3-15.4,4.3
|
||||
c-4.4,0-5.9-1.7-5.9-1.7c-2.6-2.4-4-2-17.1-25.1C59.1,88.8,43.9,65.5,43.5,65.1c-1-0.9-0.9-0.9,32,57.7c5.3,12.2,1.1,16.7,1.1,18.4
|
||||
c0,3.5-1,5.4-5.4,10.3c-7.3,8.1-10.6,17.2-12.9,36.1c-2.6,21.1-10.1,36.1-30.7,61.6c-12.1,15-14,17.7-17.1,23.7
|
||||
c-3.8,7.6-4.9,11.9-5.3,21.4c-0.5,10.1,0.4,16.7,3.5,26.4c2.7,8.5,5.6,14.1,12.9,25.3c6.3,9.7,9.9,16.9,9.9,19.7
|
||||
c0,2.2,0.4,2.2,10.2,0.1c23.3-5.2,42.2-14.4,52.8-25.7c6.6-7,8.1-10.8,8.2-20.4c0-6.3-0.2-7.6-1.9-11.2c-2.8-5.9-7.8-10.7-18.9-18.3
|
||||
c-14.5-9.9-20.8-17.9-22.5-28.8c-1.4-9,0.2-15.3,8.3-32.1c8.3-17.4,10.4-24.8,11.8-42.3c0.9-11.3,2.2-15.8,5.4-19.3
|
||||
c3.4-3.7,6.5-5,14.9-6.1c13.7-1.9,22.5-5.4,29.7-12c6.2-5.7,8.9-11.2,9.3-19.5l0.3-6.3l-3.5-4C122.8,105.1,0.8,0,0,0
|
||||
C-0.2,0,4,5.2,9.2,11.5z M38.5,305.9c2.9-5,1.3-11.5-3.4-14.7c-4.5-3-11.5-1.6-11.5,2.3c0,1.2,0.7,2.1,2.1,2.8
|
||||
c2.5,1.3,2.7,2.7,0.7,5.7c-2,3-1.8,5.6,0.5,7.4C30.5,312.3,35.7,310.7,38.5,305.9z"/>
|
||||
<path class="st1" d="M147.6,164.9c-6.5,2-12.7,8.8-14.7,15.9c-1.2,4.4-0.5,12,1.3,14.3c2.9,3.8,5.6,4.8,13.1,4.8
|
||||
c14.7-0.1,27.5-6.4,29-14.2c1.2-6.4-4.4-15.3-12.1-19.2C160.2,164.4,151.7,163.6,147.6,164.9z M164.8,178.3c2.3-3.2,1.3-6.7-2.6-9
|
||||
c-7.3-4.5-18.4-0.8-18.4,6.1c0,3.4,5.8,7.2,11.1,7.2C158.4,182.6,163.2,180.5,164.8,178.3z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.9 KiB |
4
dist/index.html
vendored
@@ -5,8 +5,8 @@
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>ЭКСА — Ваш мост в мир цифровых активов</title>
|
||||
<script type="module" crossorigin src="/assets/index-Hdj79d7b.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-3J8Zo9Sf.css">
|
||||
<script type="module" crossorigin src="/assets/index-D__qMdwn.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-CfMiYyAp.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
28
eslint.config.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import js from '@eslint/js'
|
||||
import globals from 'globals'
|
||||
import reactHooks from 'eslint-plugin-react-hooks'
|
||||
import reactRefresh from 'eslint-plugin-react-refresh'
|
||||
import tseslint from 'typescript-eslint'
|
||||
|
||||
export default tseslint.config(
|
||||
{ ignores: ['dist'] },
|
||||
{
|
||||
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
||||
files: ['**/*.{ts,tsx}'],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2020,
|
||||
globals: globals.browser,
|
||||
},
|
||||
plugins: {
|
||||
'react-hooks': reactHooks,
|
||||
'react-refresh': reactRefresh,
|
||||
},
|
||||
rules: {
|
||||
...reactHooks.configs.recommended.rules,
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
},
|
||||
},
|
||||
)
|
||||
37
package-lock.json
generated
@@ -11,8 +11,10 @@
|
||||
"@reduxjs/toolkit": "^2.5.1",
|
||||
"@tanstack/react-query": "^5.100.9",
|
||||
"axios": "^1.7.9",
|
||||
"qrcode.react": "^4.2.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-easy-crop": "^5.5.7",
|
||||
"react-redux": "^9.2.0",
|
||||
"react-router-dom": "^7.1.5",
|
||||
"zod": "^3.24.1"
|
||||
@@ -3055,6 +3057,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/normalize-wheel": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/normalize-wheel/-/normalize-wheel-1.0.1.tgz",
|
||||
"integrity": "sha512-1OnlAPZ3zgrk8B91HyRj+eVv+kS5u+Z0SCsak6Xil/kmgEia50ga7zfkumayonZrImffAxPU/5WcyGhzetHNPA==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/optionator": {
|
||||
"version": "0.9.4",
|
||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
||||
@@ -3216,6 +3224,15 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/qrcode.react": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-4.2.0.tgz",
|
||||
"integrity": "sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "19.2.5",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz",
|
||||
@@ -3237,6 +3254,20 @@
|
||||
"react": "^19.2.5"
|
||||
}
|
||||
},
|
||||
"node_modules/react-easy-crop": {
|
||||
"version": "5.5.7",
|
||||
"resolved": "https://registry.npmjs.org/react-easy-crop/-/react-easy-crop-5.5.7.tgz",
|
||||
"integrity": "sha512-kYo4NtMeXFQB7h1U+h5yhUkE46WQbQdq7if54uDlbMdZHdRgNehfvaFrXnFw5NR1PNoUOJIfTwLnWmEx/MaZnA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"normalize-wheel": "^1.0.1",
|
||||
"tslib": "^2.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.4.0",
|
||||
"react-dom": ">=16.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-redux": {
|
||||
"version": "9.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
|
||||
@@ -3516,6 +3547,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/type-check": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||
|
||||
@@ -13,8 +13,10 @@
|
||||
"@reduxjs/toolkit": "^2.5.1",
|
||||
"@tanstack/react-query": "^5.100.9",
|
||||
"axios": "^1.7.9",
|
||||
"qrcode.react": "^4.2.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-easy-crop": "^5.5.7",
|
||||
"react-redux": "^9.2.0",
|
||||
"react-router-dom": "^7.1.5",
|
||||
"zod": "^3.24.1"
|
||||
|
||||
255
politika-cookie.txt
Normal file
@@ -0,0 +1,255 @@
|
||||
ПОЛИТИКА ИСПОЛЬЗОВАНИЯ ФАЙЛОВ COOKIE
|
||||
====================================
|
||||
|
||||
Общие положения и терминология
|
||||
------------------------------
|
||||
|
||||
Настоящая Политика использования файлов cookie (далее — Политика) устанавливает порядок обработки файлов cookie и содержащихся в них персональных данных ООО «БИТФОРС» (далее — Оператор, мы) при использовании пользователями (далее — Субъекты персональных данных, вы) интернет-ресурса https://bitforce-foundation.ru.
|
||||
|
||||
Файлы cookie — это текстовые файлы небольшого размера, которые устанавливаются на пользовательское устройство (телефон, компьютер, планшет) при посещении интернет-ресурса или совершении на нем определенных действий. Файлы cookie остаются сохраненными на устройстве даже после покидания ресурса, что позволяет «узнавать» пользователя при последующих посещениях.
|
||||
|
||||
К персональным данным относится не сам файл cookie, а его содержимое — уникальные идентификаторы, IP-адреса, информация о предпочтениях пользователя и другие данные, позволяющие прямо или косвенно идентифицировать физическое лицо.
|
||||
|
||||
|
||||
Оператор персональных данных
|
||||
----------------------------
|
||||
|
||||
Оператором персональных данных, содержащихся в файлах cookie, является:
|
||||
|
||||
ООО «БИТФОРС»
|
||||
ИНН: 9810001062
|
||||
ОГРН: 1257800060990
|
||||
Юридический адрес: 196246, город Санкт-Петербург, Московское ш, д. 25 к. 1 литера В, помещ. 3-н
|
||||
|
||||
Оператор определяет цели обработки персональных данных, их состав, а также действия (операции) с персональными данными, включая случаи использования сторонних файлов cookie (third-party cookies), формат и механика работы которых определены третьими лицами (например, Яндекс.Метрика).
|
||||
|
||||
|
||||
Категории файлов cookie и их назначение
|
||||
---------------------------------------
|
||||
|
||||
На нашем интернет-ресурсе используются следующие категории файлов cookie:
|
||||
|
||||
|
||||
1. Строго необходимые (технические) файлы cookie
|
||||
------------------------------------------------
|
||||
|
||||
Данные файлы обеспечивают работу интернет-ресурса и предоставление необходимого уровня сервиса: авторизацию, навигацию, отображение контента в соответствии с параметрами устройства, обеспечение безопасности.
|
||||
|
||||
Обработка таких файлов cookie осуществляется на основании п. 5 ч. 1 ст. 6 ФЗ № 152 (заключение и исполнение договора) — Пользовательского соглашения, устанавливающего правила использования интернет-ресурса. Согласие на использование строго необходимых файлов cookie не требуется.
|
||||
|
||||
Примеры: файлы сессий (PHPSESSID), настройки безопасности, файлы аутентификации.
|
||||
|
||||
|
||||
2. Функциональные файлы cookie
|
||||
------------------------------
|
||||
|
||||
Используются для запоминания пользовательских предпочтений и персонализации взаимодействия с сайтом: сохранение выбранного языка, региона, настроек отображения, размера шрифта.
|
||||
|
||||
Обработка осуществляется на основании согласия субъекта персональных данных (п. 1 ч. 1 ст. 6 ФЗ № 152), поскольку данная обработка не является строго необходимой для функционирования сайта.
|
||||
|
||||
Примеры: настройки языка интерфейса, предпочтения отображения, настройки доступности.
|
||||
|
||||
|
||||
3. Аналитические файлы cookie
|
||||
-----------------------------
|
||||
|
||||
Собирают информацию о взаимодействии пользователей с интернет-ресурсом для анализа его использования, выявления популярных разделов, обнаружения ошибок и улучшения пользовательского опыта. Могут содержать персональные данные, включая IP-адреса пользователей.
|
||||
|
||||
Обработка осуществляется на основании согласия субъекта персональных данных (п. 1 ч. 1 ст. 6 ФЗ № 152), так как данная обработка используется Оператором для получения конкурентного преимущества и не связана напрямую с предоставлением пользователю сервиса.
|
||||
|
||||
|
||||
4. Маркетинговые файлы cookie
|
||||
-----------------------------
|
||||
|
||||
Используются для отслеживания пользователей в целях персонализированной рекламы, анализа эффективности рекламных кампаний, ретаргетинга. Обрабатывают персональные данные пользователей.
|
||||
|
||||
Обработка осуществляется исключительно на основании согласия субъекта персональных данных (п. 1 ч. 1 ст. 6 ФЗ № 152).
|
||||
|
||||
Примеры: пиксели социальных сетей, рекламные идентификаторы, файлы ретаргетинга.
|
||||
|
||||
|
||||
Правовые основания обработки персональных данных
|
||||
------------------------------------------------
|
||||
|
||||
Обработка персональных данных, содержащихся в файлах cookie, осуществляется на следующих правовых основаниях в соответствии со ст. 6 ФЗ № 152:
|
||||
|
||||
1. Согласие субъекта персональных данных (п. 1 ч. 1 ст. 6) — для функциональных, аналитических и маркетинговых файлов cookie. Согласие должно удовлетворять критериям конкретности, предметности, информированности, сознательности и однозначности в соответствии с ч. 1 ст. 9 ФЗ № 152.
|
||||
|
||||
2. Заключение и исполнение договора (п. 5 ч. 1 ст. 6) — для строго необходимых файлов cookie, обеспечивающих работу интернет-ресурса и исполнение Пользовательского соглашения.
|
||||
|
||||
3. Законные интересы оператора (п. 7 ч. 1 ст. 6) — в исключительных случаях, когда отсутствуют иные основания и при условии, что не нарушаются права и свободы субъекта персональных данных.
|
||||
|
||||
Важно: В соответствии с п. 5 ч. 1 ст. 6 ФЗ № 152 запрещается включать в договор положения, допускающие бездействие субъекта персональных данных в качестве формы согласия.
|
||||
|
||||
|
||||
Порядок получения согласия
|
||||
--------------------------
|
||||
|
||||
Согласие на обработку персональных данных, содержащихся в файлах cookie, получается в соответствии с требованиями ФЗ № 152 и практикой Роскомнадзора:
|
||||
|
||||
Принципы получения согласия:
|
||||
• Согласие должно быть получено до начала обработки персональных данных;
|
||||
• Информация об использовании файлов cookie размещается на первом уровне интернет-ресурса (всплывающее уведомление);
|
||||
• Предоставляется возможность выбора категорий файлов cookie;
|
||||
• Используются активные формулировки ("используя сайт", "продолжая пользоваться сайтом");
|
||||
• Исключаются пассивные формулировки ("оставаясь на сайте", "находясь на сайте").
|
||||
|
||||
Критерии действительного согласия:
|
||||
• Добровольность — согласие дается по свободной воле субъекта;
|
||||
• Конкретность — четко определены цели обработки;
|
||||
• Информированность — предоставлена полная информация об обработке;
|
||||
• Однозначность — согласие выражено в недвусмысленной форме.
|
||||
|
||||
|
||||
Сторонние файлы cookie
|
||||
----------------------
|
||||
|
||||
Использование сторонних сервисов:
|
||||
Наш интернет-ресурс использует файлы cookie сторонних сервисов, включая:
|
||||
• Яндекс.Метрика (ООО «ЯНДЕКС», Россия);
|
||||
• Социальные сети и сервисы интеграции.
|
||||
|
||||
Обеспечение защиты:
|
||||
• Получено согласие на передачу;
|
||||
• Применяются дополнительные меры защиты данных;
|
||||
• Контролируется соблюдение принципов обработки персональных данных получателями.
|
||||
|
||||
Ответственность за сторонние файлы cookie:
|
||||
Оператор несет ответственность за использование сторонних файлов cookie в соответствии с законодательством о персональных данных, поскольку определяет цели и средства их обработки на своем интернет-ресурсе.
|
||||
|
||||
|
||||
Сроки обработки и хранения
|
||||
--------------------------
|
||||
|
||||
Сроки обработки персональных данных, содержащихся в файлах cookie, определяются целями обработки и требованиями законодательства:
|
||||
|
||||
Категории по срокам хранения:
|
||||
• Сеансовые cookie — удаляются автоматически при закрытии браузера;
|
||||
• Постоянные cookie — хранятся установленный период или до удаления пользователем.
|
||||
|
||||
Конкретные сроки:
|
||||
• Необходимые файлы cookie — до 12 месяцев;
|
||||
• Функциональные файлы cookie — до 12 месяцев;
|
||||
• Аналитические файлы cookie — до 24 месяцев;
|
||||
• Маркетинговые файлы cookie — до 24 месяцев.
|
||||
|
||||
Автоматическое удаление:
|
||||
По истечении установленных сроков файлы cookie удаляются автоматически. Пользователь может удалить файлы cookie досрочно через настройки браузера или отозвать согласие на их обработку.
|
||||
|
||||
|
||||
Права субъектов персональных данных
|
||||
-----------------------------------
|
||||
|
||||
В соответствии с ФЗ № 152 субъекты персональных данных имеют следующие права:
|
||||
|
||||
Право на информацию (ст. 14 ФЗ № 152):
|
||||
• Получение информации о обработке персональных данных;
|
||||
• Сведения о правовых основаниях и целях обработки;
|
||||
• Информация о сроках обработки и составе данных.
|
||||
|
||||
Право на доступ (ст. 14 ФЗ № 152):
|
||||
• Получение подтверждения факта обработки;
|
||||
• Ознакомление с обрабатываемыми персональными данными;
|
||||
• Получение информации об источниках персональных данных.
|
||||
|
||||
Право на уточнение, блокирование, удаление (ст. 14 ФЗ № 152):
|
||||
• Требование уточнения неточных данных;
|
||||
• Блокирование недостоверных данных;
|
||||
• Удаление незаконно полученных данных.
|
||||
|
||||
Право на отзыв согласия:
|
||||
• Отзыв согласия в любое время;
|
||||
• Прекращение обработки после отзыва согласия;
|
||||
• Сохранение права на обжалование действий оператора.
|
||||
|
||||
Право на обжалование:
|
||||
• Обращение в Роскомнадзор или его территориальные органы;
|
||||
• Обращение в суд для защиты нарушенных прав;
|
||||
• Требование возмещения ущерба.
|
||||
|
||||
|
||||
Способы управления файлами cookie
|
||||
---------------------------------
|
||||
|
||||
Управление через настройки сайта:
|
||||
• Использование баннера согласия на файлы cookie;
|
||||
• Изменение настроек в любое время через интерфейс сайта;
|
||||
• Отзыв согласия на использование отдельных категорий файлов cookie.
|
||||
|
||||
Управление через браузер:
|
||||
Большинство браузеров позволяют контролировать файлы cookie:
|
||||
• Блокировка — запрет установки новых файлов cookie;
|
||||
• Удаление — очистка существующих файлов cookie;
|
||||
• Уведомления — получение предупреждений при установке файлов cookie;
|
||||
• Селективная настройка — разрешение файлов cookie только для определенных сайтов.
|
||||
|
||||
Инструкции для популярных браузеров:
|
||||
• Google Chrome: Настройки → Конфиденциальность и безопасность → Файлы cookie
|
||||
• Mozilla Firefox: Настройки → Приватность и Защита → Файлы cookie
|
||||
• Safari: Настройки → Конфиденциальность → Файлы cookie
|
||||
• Microsoft Edge: Настройки → Файлы cookie и разрешения сайтов
|
||||
|
||||
Важное замечание: Блокировка необходимых файлов cookie может привести к ограничению функциональности интернет-ресурса.
|
||||
|
||||
|
||||
Меры безопасности
|
||||
-----------------
|
||||
|
||||
Оператор применяет правовые, организационные и технические меры для защиты персональных данных в соответствии с требованиями ФЗ № 152 и постановления Правительства РФ № 1119:
|
||||
|
||||
Правовые меры:
|
||||
• Назначение ответственного за организацию обработки персональных данных;
|
||||
• Ознакомление сотрудников с требованиями законодательства;
|
||||
• Заключение соглашений о неразглашении персональных данных.
|
||||
|
||||
Организационные меры:
|
||||
• Определение перечня лиц, допущенных к обработке персональных данных;
|
||||
• Установление правил доступа к персональным данным;
|
||||
• Контроль за соблюдением требований по защите персональных данных.
|
||||
|
||||
Технические меры:
|
||||
• Использование средств защиты информации;
|
||||
• Применение криптографических средств защиты;
|
||||
• Обеспечение целостности и доступности персональных данных;
|
||||
• Регулярное обновление систем защиты информации.
|
||||
|
||||
|
||||
Обновления политики
|
||||
-------------------
|
||||
|
||||
Настоящая Политика может изменяться в связи с:
|
||||
• Изменениями в законодательстве Российской Федерации;
|
||||
• Изменениями в практике Роскомнадзора;
|
||||
• Развитием технологий обработки данных;
|
||||
• Изменениями в бизнес-процессах Оператора.
|
||||
|
||||
Процедура уведомления об изменениях:
|
||||
• Размещение обновленной Политики на интернет-ресурсе;
|
||||
• Указание даты последнего обновления;
|
||||
• Уведомление пользователей о существенных изменениях через интерфейс сайта;
|
||||
• При необходимости — получение нового согласия на обработку.
|
||||
|
||||
Рекомендуем регулярно проверять данную страницу для ознакомления с актуальной информацией.
|
||||
|
||||
|
||||
Контактная информация и обращения
|
||||
---------------------------------
|
||||
|
||||
Для реализации прав субъекта персональных данных обращайтесь к нам:
|
||||
|
||||
ООО «БИТФОРС»
|
||||
ИНН: 9810001062
|
||||
ОГРН: 1257800060990
|
||||
Юридический адрес: 196246, город Санкт-Петербург, Московское ш, д. 25 к. 1 литера В, помещ. 3-н
|
||||
|
||||
Контакты:
|
||||
• Email компании: company@bitforcefoundation.ru
|
||||
|
||||
Порядок рассмотрения обращений:
|
||||
• Срок рассмотрения обращений — до 30 дней с момента получения;
|
||||
• Обращения рассматриваются в письменной форме;
|
||||
• Ответ направляется способом, указанным в обращении;
|
||||
• При отказе в удовлетворении требований указываются мотивированные основания.
|
||||
|
||||
Обращения в контролирующие органы:
|
||||
Федеральная служба по надзору в сфере связи, информационных технологий и массовых коммуникаций (Роскомнадзор)
|
||||
549
politika-obrabotki-personalnyh-dannyh.txt
Normal file
@@ -0,0 +1,549 @@
|
||||
ПОЛИТИКА ОБРАБОТКИ ПЕРСОНАЛЬНЫХ ДАННЫХ
|
||||
======================================
|
||||
|
||||
ООО «БИТФОРС»
|
||||
|
||||
1. Общие положения
|
||||
------------------
|
||||
|
||||
1.1. Настоящая Политика обработки персональных данных (далее — Политика) разработана в соответствии с Федеральным законом от 27.07.2006 № 152-ФЗ «О персональных данных» (далее — Закон о персональных данных) и определяет порядок обработки персональных данных и меры по обеспечению безопасности персональных данных, предпринимаемые ООО «БИТФОРС» (далее — Оператор).
|
||||
|
||||
1.2. Оператор ставит своей важнейшей целью и условием осуществления своей деятельности соблюдение прав и свобод человека и гражданина при обработке его персональных данных, в том числе защиты права на неприкосновенность частной жизни, личную и семейную тайну.
|
||||
|
||||
1.3. Настоящая Политика действует в отношении всех персональных данных, которые обрабатываются Оператором с использованием средств автоматизации и без использования таких средств, при условии, что обработка персональных данных без использования средств автоматизации соответствует характеру действий (операций), совершаемых с персональными данными с использованием средств автоматизации.
|
||||
|
||||
|
||||
1.4. Основные понятия
|
||||
---------------------
|
||||
|
||||
Автоматизированная обработка персональных данных — обработка персональных данных с помощью средств вычислительной техники.
|
||||
|
||||
Блокирование персональных данных — временное прекращение обработки персональных данных (за исключением случаев, если обработка необходима для уточнения персональных данных).
|
||||
|
||||
Веб-сайт — совокупность графических и информационных материалов, а также программ для ЭВМ и баз данных, обеспечивающих их доступность в сети интернет по сетевому адресу https://bitforce-foundation.ru.
|
||||
|
||||
Информационная система персональных данных — совокупность содержащихся в базах данных персональных данных и обеспечивающих их обработку информационных технологий и технических средств.
|
||||
|
||||
Обезличивание персональных данных — действия, в результате которых невозможно определить без использования дополнительной информации принадлежность персональных данных конкретному субъекту персональных данных.
|
||||
|
||||
Обработка персональных данных — любое действие (операция) или совокупность действий (операций), совершаемых с использованием средств автоматизации или без использования таких средств с персональными данными, включая сбор, запись, систематизацию, накопление, хранение, уточнение (обновление, изменение), извлечение, использование, передачу (распространение, предоставление, доступ), обезличивание, блокирование, удаление, уничтожение персональных данных.
|
||||
|
||||
Оператор — государственный орган, муниципальный орган, юридическое или физическое лицо, самостоятельно или совместно с другими лицами организующие и/или осуществляющие обработку персональных данных, а также определяющие цели обработки персональных данных, состав персональных данных, подлежащих обработке, действия (операции), совершаемые с персональными данными.
|
||||
|
||||
Персональные данные — любая информация, относящаяся к прямо или косвенно определенному или определяемому физическому лицу (субъекту персональных данных).
|
||||
|
||||
Персональные данные, разрешенные субъектом персональных данных для распространения — персональные данные, доступ неограниченного круга лиц к которым предоставлен субъектом персональных данных путем дачи согласия на обработку персональных данных, разрешенных субъектом персональных данных для распространения.
|
||||
|
||||
Пользователь — любой посетитель веб-сайта https://bitforce-foundation.ru.
|
||||
|
||||
Предоставление персональных данных — действия, направленные на раскрытие персональных данных определенному лицу или определенному кругу лиц.
|
||||
|
||||
Распространение персональных данных — действия, направленные на раскрытие персональных данных неопределенному кругу лиц (передача персональных данных) или на ознакомление с персональными данными неограниченного круга лиц, в том числе обнародование персональных данных в средствах массовой информации, размещение в информационно-телекоммуникационных сетях или предоставление доступа к персональным данным каким-либо иным способом.
|
||||
|
||||
Уничтожение персональных данных — действия, в результате которых невозможно восстановить содержание персональных данных в информационной системе персональных данных и/или результате которых уничтожаются материальные носители персональных данных.
|
||||
|
||||
|
||||
2. Сведения об операторе
|
||||
------------------------
|
||||
|
||||
2.1. Полное наименование оператора: Общество с ограниченной ответственностью «БИТФОРС»
|
||||
|
||||
2.2. Сокращенное наименование: ООО «БИТФОРС»
|
||||
|
||||
2.3. ИНН: 9810001062
|
||||
|
||||
2.4. ОГРН: 1257800060990
|
||||
|
||||
2.5. Реестр ОПД: Посмотреть в реестре Роскомнадзора (https://pd.rkn.gov.ru/operators-registry/operators-list/?act=search&name_full=%D0%91%D0%B8%D1%82%D1%84%D0%BE%D1%80%D1%81&inn=9810001062®n=)
|
||||
|
||||
2.6. Юридический адрес: 196246, город Санкт-Петербург, Московское шоссе, дом 25, корпус 1, литера В, помещение 3-н
|
||||
|
||||
2.7. Почтовый адрес: 196246, город Санкт-Петербург, Московское шоссе, дом 25, корпус 1, литера В, помещение 3-н
|
||||
|
||||
2.8. Контактная информация:
|
||||
• Электронная почта: company@bitforcefoundation.ru
|
||||
• Веб-сайт: https://bitforce-foundation.ru
|
||||
|
||||
2.9. Ответственный за организацию обработки персональных данных: назначается приказом генерального директора
|
||||
|
||||
2.10. Контакты ответственного лица по вопросам обработки персональных данных:
|
||||
• Электронная почта: company@bitforcefoundation.ru
|
||||
|
||||
|
||||
3. Общие цели обработки персональных данных
|
||||
-------------------------------------------
|
||||
|
||||
3.1. Оператор обрабатывает персональные данных для достижения следующих общих целей:
|
||||
|
||||
3.1.1. Основная деятельность:
|
||||
• Предоставление услуг по конвертации иного имущества;
|
||||
• Осуществление операций на криптовалютных рынках;
|
||||
• Предоставление услуг в области блокчейн технологий;
|
||||
• Обеспечение функционирования интернет-платформы и мобильных приложений.
|
||||
|
||||
3.1.2. Обеспечение безопасности:
|
||||
• Предотвращение мошенничества и отмывания денежных средств;
|
||||
• Обеспечение безопасности платежных операций;
|
||||
• Выполнение требований по противодействию легализации доходов, полученных преступным путем;
|
||||
• Идентификация и верификация клиентов.
|
||||
|
||||
3.1.3. Соблюдение законодательства:
|
||||
• Исполнение требований российского и международного законодательства;
|
||||
• Взаимодействие с контролирующими и правоохранительными органами;
|
||||
• Ведение обязательной отчетности и документооборота;
|
||||
• Соблюдение налогового законодательства.
|
||||
|
||||
3.1.4. Развитие бизнеса:
|
||||
• Анализ и улучшение качества предоставляемых услуг;
|
||||
• Разработка новых продуктов и сервисов;
|
||||
• Проведение маркетинговых исследований;
|
||||
• Осуществление клиентской поддержки.
|
||||
|
||||
|
||||
4. Цели сбора персональных данных
|
||||
---------------------------------
|
||||
|
||||
4.1. Регистрация и идентификация пользователей:
|
||||
• Создание учетной записи на веб-сайте;
|
||||
• Верификация личности в соответствии с требованиями законодательства;
|
||||
• Подтверждение права на осуществление операций;
|
||||
• Обеспечение доступа к персональному кабинету.
|
||||
|
||||
4.2. Обработка платежей и финансовых операций:
|
||||
• Осуществление операций по конвертации криптовалют;
|
||||
• Проведение расчетов и переводов денежных средств;
|
||||
• Ведение учета и истории транзакций;
|
||||
• Расчет комиссий и сборов.
|
||||
|
||||
4.3. Обеспечение безопасности и соблюдение требований:
|
||||
• Проверка на причастность к запрещенной деятельности;
|
||||
• Мониторинг подозрительных операций;
|
||||
• Выполнение процедур по противодействию легализации доходов;
|
||||
• Архивирование данных для правоохранительных органов.
|
||||
|
||||
4.4. Коммуникация с клиентами:
|
||||
• Предоставление технической поддержки;
|
||||
• Уведомления о состоянии операций и счетов;
|
||||
• Информирование об изменениях в условиях предоставления услуг;
|
||||
• Рассылка маркетинговых материалов (при наличии согласия).
|
||||
|
||||
4.5. Аналитика и улучшение сервиса:
|
||||
• Анализ пользовательского поведения для улучшения интерфейса;
|
||||
• Создание аналитических отчетов;
|
||||
• Исследование рынка и потребностей клиентов;
|
||||
• Разработка персонализированных предложений.
|
||||
|
||||
4.6. Исполнение договорных обязательств:
|
||||
• Выполнение условий пользовательского соглашения;
|
||||
• Предоставление заказанных услуг;
|
||||
• Рассмотрение жалоб и претензий;
|
||||
• Урегулирование споров.
|
||||
|
||||
|
||||
5. Правовые основания обработки персональных данных
|
||||
---------------------------------------------------
|
||||
|
||||
5.1. Правовыми основаниями обработки персональных данных Оператором являются:
|
||||
|
||||
5.1.1. Согласие субъекта персональных данных (п. 1 ч. 1 ст. 6 Закона о персональных данных):
|
||||
• Обработка персональных данных в маркетинговых целях;
|
||||
• Использование файлов cookie и метрик;
|
||||
• Персонализация сервисов и предложений;
|
||||
• Проведение опросов и исследований.
|
||||
|
||||
5.1.2. Необходимость исполнения договора (п. 5 ч. 1 ст. 6 Закона о персональных данных):
|
||||
• Регистрация и ведение учетных записей пользователей;
|
||||
• Осуществление финансовых операций и переводов;
|
||||
• Предоставление доступа к платформе и сервисам;
|
||||
• Техническая поддержка и обслуживание клиентов.
|
||||
|
||||
5.1.3. Соблюдение правовой обязанности (п. 2 ч. 1 ст. 6 Закона о персональных данных):
|
||||
• Выполнение требований валютного законодательства;
|
||||
• Противодействие легализации доходов, полученных преступным путем;
|
||||
• Соблюдение требований по налоговому учету и отчетности;
|
||||
• Взаимодействие с правоохранительными органами.
|
||||
|
||||
5.1.4. Защита жизненно важных интересов (п. 3 ч. 1 ст. 6 Закона о персональных данных):
|
||||
• Предотвращение мошенничества и финансовых преступлений;
|
||||
• Обеспечение безопасности платежных систем;
|
||||
• Защита от кибератак и несанкционированного доступа.
|
||||
|
||||
5.1.5. Законные интересы оператора (п. 7 ч. 1 ст. 6 Закона о персональных данных):
|
||||
• Обеспечение информационной безопасности;
|
||||
• Аналитика для улучшения качества услуг;
|
||||
• Защита прав и законных интересов Оператора;
|
||||
• Взыскание задолженности.
|
||||
|
||||
5.2. При обработке персональных данных на основании согласия субъекта, такое согласие оформляется в соответствии с требованиями ст. 9 Закона о персональных данных.
|
||||
|
||||
5.3. При обработке персональных данных на основании законных интересов Оператор обеспечивает баланс между своими интересами и правами субъектов персональных данных.
|
||||
|
||||
|
||||
6. Объем и категории обрабатываемых персональных данных
|
||||
-------------------------------------------------------
|
||||
|
||||
6.1. Категории субъектов персональных данных:
|
||||
|
||||
6.1.1. Пользователи веб-сайта и мобильного приложения:
|
||||
• Зарегистрированные пользователи;
|
||||
• Посетители сайта без регистрации;
|
||||
• Потенциальные клиенты;
|
||||
• Бывшие клиенты.
|
||||
|
||||
6.1.2. Клиенты:
|
||||
• Физические лица, пользующиеся услугами Оператора;
|
||||
• Индивидуальные предприниматели;
|
||||
• Представители юридических лиц;
|
||||
• Выгодоприобретатели.
|
||||
|
||||
6.1.3. Сотрудники и партнеры:
|
||||
• Сотрудники Оператора;
|
||||
• Кандидаты на трудоустройство;
|
||||
• Представители контрагентов;
|
||||
• Консультанты и советники.
|
||||
|
||||
6.2. Категории и состав обрабатываемых персональных данных:
|
||||
|
||||
6.2.1. Идентификационные данные:
|
||||
• Фамилия, имя, отчество;
|
||||
• Дата рождения;
|
||||
• Место рождения;
|
||||
• Гражданство;
|
||||
• Пол.
|
||||
|
||||
6.2.2. Паспортные данные:
|
||||
• Серия и номер паспорта;
|
||||
• Дата выдачи паспорта;
|
||||
• Код подразделения;
|
||||
• Кем выдан паспорт;
|
||||
• Адрес регистрации;
|
||||
|
||||
6.2.3. Контактная информация:
|
||||
• Номера телефонов (мобильный, домашний, рабочий);
|
||||
• Адреса электронной почты;
|
||||
|
||||
6.2.4. Финансовая информация:
|
||||
• Номера банковских счетов и карт;
|
||||
• Реквизиты кошельков криптовалют;
|
||||
• История операций и транзакций;
|
||||
• Данные о доходах и источниках средств;
|
||||
• Налоговая информация.
|
||||
|
||||
6.2.5. Техническая информация:
|
||||
• IP-адреса устройств;
|
||||
• Данные о браузере и операционной системе;
|
||||
• Файлы cookie и локальное хранилище;
|
||||
• Логи действий на сайте;
|
||||
• Геолокационные данные.
|
||||
|
||||
6.2.6. Дополнительные данные:
|
||||
• Фотографии для верификации;
|
||||
• Биометрические данные (при использовании);
|
||||
• Видеозаписи видеоидентификации;
|
||||
• Данные о семейном положении и составе семьи;
|
||||
• Профессиональная информация.
|
||||
|
||||
6.3. Источники получения персональных данных:
|
||||
• Непосредственно от субъектов персональных данных;
|
||||
• Из общедоступных источников;
|
||||
• От третьих лиц с согласия субъекта;
|
||||
• Из государственных информационных систем;
|
||||
• От партнеров и контрагентов.
|
||||
|
||||
|
||||
7. Порядок и условия обработки персональных данных
|
||||
--------------------------------------------------
|
||||
|
||||
7.1. Принципы обработки персональных данных:
|
||||
• Обработка персональных данных осуществляется на законной и справедливой основе;
|
||||
• Обработка ограничивается достижением конкретных, заранее определенных и законных целей;
|
||||
• Не допускается обработка персональных данных, несовместимая с целями сбора;
|
||||
• Содержание и объем обрабатываемых персональных данных соответствуют заявленным целям;
|
||||
• Обрабатываемые персональные данные являются точными и актуальными.
|
||||
|
||||
7.2. Способы обработки персональных данных:
|
||||
|
||||
7.2.1. Автоматизированная обработка:
|
||||
• Сбор данных через веб-формы и мобильные приложения;
|
||||
• Автоматическая обработка платежей и транзакций;
|
||||
• Автоматизированный анализ и мониторинг операций;
|
||||
• Генерация отчетов и статистики.
|
||||
|
||||
7.2.2. Неавтоматизированная обработка:
|
||||
• Ручная верификация документов;
|
||||
• Обработка обращений службы поддержки;
|
||||
• Подготовка документов для регулирующих органов;
|
||||
• Архивирование документов на электронных носителях (при необходимости на бумажных носителях).
|
||||
|
||||
7.3. Условия обработки персональных данных:
|
||||
|
||||
7.3.1. Получение согласия:
|
||||
• Согласие получается в письменной форме или путем совершения конклюдентных действий;
|
||||
• Субъект информируется о целях, способах и сроках обработки;
|
||||
• Предоставляется возможность отозвать согласие;
|
||||
• Ведется учет полученных согласий.
|
||||
|
||||
7.3.2. Обработка без согласия:
|
||||
• Осуществляется только в случаях, предусмотренных законодательством;
|
||||
• Документируются правовые основания обработки;
|
||||
• Обеспечивается соответствие объема обработки заявленным целям;
|
||||
• Ведется учет случаев обработки без согласия.
|
||||
|
||||
7.4. Сроки обработки персональных данных:
|
||||
• Персональные данные обрабатываются в течение времени, необходимого для достижения целей обработки;
|
||||
• После достижения целей обработки персональные данные подлежат уничтожению или обезличиванию;
|
||||
• Сроки хранения определяются требованиями законодательства и внутренними регламентами;
|
||||
• Ведется учет сроков обработки для каждой категории данных.
|
||||
|
||||
7.5. Места обработки персональных данных:
|
||||
• Основные серверы и хранилища данных расположены на территории Российской Федерации;
|
||||
• Резервные копии могут храниться в дата-центрах на территории РФ;
|
||||
• Обработка может осуществляться удаленно с соблюдением требований безопасности;
|
||||
|
||||
|
||||
8. Актуализация, исправление, удаление и уничтожение персональных данных
|
||||
------------------------------------------------------------
|
||||
|
||||
8.1. Актуализация персональных данных:
|
||||
|
||||
8.1.1. Обязанности субъекта:
|
||||
• Предоставление актуальных и достоверных персональных данных;
|
||||
• Незамедлительное уведомление об изменении персональных данных;
|
||||
• Подтверждение изменений документально при необходимости.
|
||||
|
||||
8.1.2. Процедуры актуализации:
|
||||
• Регулярная проверка актуальности данных;
|
||||
• Запросы на подтверждение данных;
|
||||
• Автоматическое обновление при получении новой информации;
|
||||
• Верификация изменений через дополнительные каналы связи.
|
||||
|
||||
8.2. Исправление персональных данных:
|
||||
|
||||
8.2.1. Основания для исправления:
|
||||
• Обнаружение неточностей в персональных данных;
|
||||
• Получение запроса от субъекта персональных данных;
|
||||
• Выявление ошибок при автоматизированной обработке;
|
||||
• Получение уточняющей информации из достоверных источников.
|
||||
|
||||
8.2.2. Процедура исправления:
|
||||
• Рассмотрение запроса в течение 30 дней;
|
||||
• Проверка обоснованности требования об исправлении;
|
||||
• Внесение изменений во все информационные системы;
|
||||
• Уведомление субъекта о проведенных исправлениях;
|
||||
• Уведомление третьих лиц, которым передавались неточные данные.
|
||||
|
||||
8.3. Удаление персональных данных:
|
||||
|
||||
8.3.1. Основания для удаления:
|
||||
• Отзыв согласия субъектом персональных данных;
|
||||
• Достижение целей обработки;
|
||||
• Истечение сроков обработки;
|
||||
• Выявление незаконности обработки;
|
||||
• Требование субъекта при наличии оснований.
|
||||
|
||||
8.3.2. Процедура удаления:
|
||||
• Проверка наличия законных оснований для продолжения обработки;
|
||||
• Удаление из всех информационных систем и баз данных;
|
||||
• Удаление резервных копий (кроме архивных);
|
||||
• Уведомление субъекта о выполненном удалении;
|
||||
• Документирование факта удаления.
|
||||
|
||||
8.4. Уничтожение персональных данных:
|
||||
|
||||
8.4.1. Случаи уничтожения:
|
||||
• Истечение сроков хранения;
|
||||
• Ликвидация организации;
|
||||
• Прекращение обработки по решению суда;
|
||||
• Техническая необходимость (замена оборудования).
|
||||
|
||||
8.4.2. Способы уничтожения:
|
||||
• Физическое уничтожение носителей информации;
|
||||
• Криптографическое уничтожение (удаление ключей шифрования);
|
||||
• Перезапись информации на носителях;
|
||||
• Размагничивание магнитных носителей;
|
||||
• Использование специализированного программного обеспечения.
|
||||
|
||||
8.4.3. Документирование уничтожения:
|
||||
• Составление актов уничтожения;
|
||||
• Ведение журналов уничтожения;
|
||||
• Фиксация времени, способа и ответственных лиц;
|
||||
• Хранение документов об уничтожении в течение установленного срока.
|
||||
|
||||
|
||||
9. Ответы на запросы субъектов персональных данных
|
||||
--------------------------------------------------
|
||||
|
||||
9.1. Права субъектов персональных данных:
|
||||
|
||||
9.1.1. Право на информацию (ст. 14 Закона о персональных данных):
|
||||
• Подтверждение факта обработки персональных данных;
|
||||
• Правовые основания и цели обработки;
|
||||
• Применяемые способы обработки;
|
||||
• Наименование и местонахождение оператора;
|
||||
• Лица, имеющие доступ к персональным данным;
|
||||
• Обрабатываемые персональные данные;
|
||||
• Источник получения персональных данных;
|
||||
• Сроки обработки персональных данных;
|
||||
• Порядок осуществления прав субъекта;
|
||||
|
||||
9.1.2. Право на доступ:
|
||||
• Получение копий обрабатываемых персональных данных;
|
||||
• Ознакомление с историей обработки;
|
||||
• Получение информации о передаче данных третьим лицам;
|
||||
• Доступ к автоматизированным решениям.
|
||||
|
||||
9.1.3. Право на исправление:
|
||||
• Требование исправления неточных данных;
|
||||
• Дополнение неполных данных;
|
||||
• Актуализация устаревших данных.
|
||||
|
||||
9.1.4. Право на удаление ("право на забвение"):
|
||||
• Требование удаления персональных данных;
|
||||
• Отзыв согласия на обработку;
|
||||
• Возражение против обработки.
|
||||
|
||||
9.1.5. Право на ограничение обработки:
|
||||
• Блокирование обработки на время проверки точности;
|
||||
• Ограничение способов обработки;
|
||||
• Приостановление передачи третьим лицам.
|
||||
|
||||
9.2. Процедура рассмотрения запросов:
|
||||
|
||||
9.2.1. Подача запроса:
|
||||
• Запрос может быть подан лично, по почте или электронно;
|
||||
• Запрос должен содержать сведения, указанные в ч. 3 ст. 14 Закона;
|
||||
• При подаче запроса в электронной форме требуется подтверждение личности;
|
||||
• Запрос может быть подан через представителя при наличии доверенности.
|
||||
|
||||
9.2.2. Сроки рассмотрения:
|
||||
• Срок рассмотрения запроса составляет 30 дней с момента получения;
|
||||
• Срок может быть продлен на 30 дней при большом объеме информации;
|
||||
• О продлении срока субъект уведомляется в течение 30 дней;
|
||||
• Безотлагательно — в случаях, указанных в законе.
|
||||
|
||||
9.2.3. Содержание ответа:
|
||||
• Подтверждение получения запроса;
|
||||
• Запрашиваемая информация в доступной форме;
|
||||
• Разъяснение порядка обжалования;
|
||||
• Мотивированный отказ (при наличии оснований).
|
||||
|
||||
9.2.4. Способы предоставления ответа:
|
||||
• В письменной форме по почте;
|
||||
• В электронной форме (при согласии субъекта);
|
||||
• Через личный кабинет на сайте;
|
||||
• При личном обращении в офис Оператора.
|
||||
|
||||
9.3. Основания для отказа в удовлетворении запроса:
|
||||
• Обработка необходима для защиты жизни, здоровья субъекта;
|
||||
• Обработка необходима для выполнения функций государства;
|
||||
• Персональные данные получены в рамках оперативно-розыскной деятельности;
|
||||
• Обработка необходима для защиты прав и законных интересов третьих лиц;
|
||||
• Исполнение судебного акта или иного акта государственного органа.
|
||||
|
||||
9.4. Плата за предоставление информации:
|
||||
• Первый запрос в течение года обрабатывается бесплатно;
|
||||
• За повторные запросы может взиматься плата в размере расходов;
|
||||
• Размер платы определяется в соответствии с законодательством;
|
||||
• Субъект уведомляется о размере платы до предоставления информации.
|
||||
|
||||
|
||||
10. Обеспечение безопасности персональных данных
|
||||
------------------------------------------------
|
||||
|
||||
10.1. Правовые меры:
|
||||
• Назначение ответственного за организацию обработки персональных данных;
|
||||
• Принятие локальных актов по вопросам обработки персональных данных;
|
||||
• Ознакомление работников с требованиями законодательства и локальными актами;
|
||||
• Применение мер ответственности за нарушение требований законодательства.
|
||||
|
||||
10.2. Организационные меры:
|
||||
• Определение перечня лиц, допущенных к обработке персональных данных;
|
||||
• Установление правил доступа к персональным данным;
|
||||
• Применение средств защиты, прошедших процедуру оценки соответствия;
|
||||
• Оценка вреда, который может быть причинен субъектам персональных данных;
|
||||
• Учет машинных носителей персональных данных;
|
||||
• Обнаружение фактов несанкционированного доступа;
|
||||
• Восстановление персональных данных.
|
||||
|
||||
10.3. Технические меры:
|
||||
• Предотвращение несанкционированного доступа к персональным данным;
|
||||
• Своевременное обнаружение фактов несанкционированного доступа;
|
||||
• Предотвращение воздействия на технические средства обработки;
|
||||
• Возможность незамедлительного восстановления персональных данных;
|
||||
• Постоянный контроль за обеспечением уровня защищенности.
|
||||
|
||||
10.4. Конкретные технические решения:
|
||||
• Использование сертифицированных средств защиты информации;
|
||||
• Шифрование персональных данных при передаче и хранении;
|
||||
• Применение межсетевых экранов и систем обнаружения вторжений;
|
||||
• Резервное копирование и обеспечение отказоустойчивости;
|
||||
• Аудит и мониторинг доступа к информационным системам;
|
||||
• Антивирусная защита и обновление программного обеспечения.
|
||||
|
||||
10.5. Контроль доступа:
|
||||
• Идентификация и аутентификация пользователей;
|
||||
• Разграничение доступа по ролям и полномочиям;
|
||||
• Протоколирование действий пользователей;
|
||||
• Регулярный пересмотр прав доступа;
|
||||
• Блокирование учетных записей при увольнении сотрудников.
|
||||
|
||||
|
||||
11. Сведения о реализуемых требованиях к защите персональных данных
|
||||
------------------------------------------------------------
|
||||
|
||||
11.1. Уровни защищенности:
|
||||
Оператор определяет уровни защищенности персональных данных в соответствии с Постановлением Правительства РФ от 01.11.2012 № 1119 и обеспечивает соответствующие им требования безопасности.
|
||||
|
||||
11.2. 1-й уровень защищенности (общедоступные персональные данные):
|
||||
• Базовые средства защиты от несанкционированного доступа;
|
||||
• Парольная защита доступа к информационным системам;
|
||||
• Антивирусная защита рабочих станций и серверов.
|
||||
|
||||
11.3. 2-й уровень защищенности (иные персональные данные, кроме специальных и биометрических):
|
||||
• Идентификация и аутентификация субъектов доступа;
|
||||
• Управление доступом субъектов доступа к ресурсам;
|
||||
• Ограничение программной среды;
|
||||
• Защита машинных носителей информации;
|
||||
• Регистрация событий безопасности;
|
||||
• Антивирусная защита;
|
||||
• Обнаружение вторжений;
|
||||
• Контроль целостности информационной системы и информации.
|
||||
|
||||
11.4. 3-й уровень защищенности (специальные категории персональных данных):
|
||||
Дополнительно к требованиям 2-го уровня:
|
||||
• Обеспечение целостности информационной системы и информации;
|
||||
• Обеспечение доступности информационной системы и информации;
|
||||
• Защита технических средств;
|
||||
• Защита информационной системы, ее средств, систем связи и передачи данных.
|
||||
|
||||
11.5. 4-й уровень защищенности (биометрические персональные данные):
|
||||
Дополнительно к требованиям 3-го уровня:
|
||||
• Предотвращение внедрения в информационную систему программных закладок;
|
||||
• Анализ защищенности информационной системы.
|
||||
|
||||
|
||||
12. Заключительные положения
|
||||
----------------------------
|
||||
|
||||
12.1. Внесение изменений в Политику:
|
||||
• Политика может изменяться в связи с изменениями в законодательстве;
|
||||
• Существенные изменения доводятся до сведения субъектов персональных данных;
|
||||
• Действующая версия Политики размещается на официальном сайте;
|
||||
• Дата последнего обновления указывается в Политике.
|
||||
|
||||
12.2. Жалобы и обращения:
|
||||
• Субъекты персональных данных могут обратиться к Оператору по вопросам обработки;
|
||||
• Жалобы рассматриваются в установленном законом порядке;
|
||||
• При неурегулировании разногласий возможно обращение в Роскомнадзор или суд.
|
||||
|
||||
12.3. Применимое право:
|
||||
• Политика регулируется законодательством Российской Федерации;
|
||||
• Споры рассматриваются в российских судах;
|
||||
• При противоречии переводов приоритет имеет русскоязычная версия.
|
||||
|
||||
12.4. Контактная информация для обращений:
|
||||
• Почтовый адрес: 196246, г. Санкт-Петербург, Московское ш., д. 25, к. 1, лит. В, пом. 3-н
|
||||
• Электронная почта: company@bitforcefoundation.ru
|
||||
|
||||
12.5. Вступление в силу:
|
||||
• Настоящая Политика вступает в силу с момента ее утверждения и размещения на официальном сайте Оператора.
|
||||
235
publichnaya-oferta.txt
Normal file
@@ -0,0 +1,235 @@
|
||||
ПУБЛИЧНЫЙ ДОГОВОР ОФЕРТЫ
|
||||
========================
|
||||
|
||||
ООО БИТФОРС
|
||||
|
||||
Агентский договор
|
||||
-----------------
|
||||
|
||||
Настоящая оферта на заключение агентского договора
|
||||
(далее – Оферта, Договор) является публичным предложением
|
||||
Общества с ограниченной ответственностью «БИТФОРС», заключить договор на условиях и в порядке, определенных настоящей Офертой.
|
||||
|
||||
Акцепт оферты производится в соответствии с пунктом 2 статьи 437 Гражданского кодекса Российской Федерации и равносилен заключению агентского договора в письменной форме.
|
||||
|
||||
|
||||
Основные понятия и определения действующего договора
|
||||
----------------------------------------------------
|
||||
|
||||
Агент – юридическое лицо или индивидуальный предприниматель, зарегистрированный на территории Российской Федерации, в установленном действующим законодательством порядке.
|
||||
|
||||
Принципал – сторона агентского договора, по поручению которой агент осуществляет юридические и иные действия от своего имени, но за счет принципала либо от имени и за счет принципала.
|
||||
|
||||
Агентский договор – соглашение, по которому агент обязуется за вознаграждение совершать по поручению принципала юридические и иные действия от своего имени, но за счет принципала либо от имени и за счет принципала в соответствии с п. 1 ст. 1005 Гражданского Кодекса Российской Федерации.
|
||||
|
||||
Личный кабинета Агента – ресурс, размещенный на сайте Принципала, предназначенный для взаимодействия Агента и Принципала.
|
||||
|
||||
Отчетный период – период для взаиморасчетов с Агентом, равный
|
||||
одному календарному кварталу с даты активации любой из услуг, предоставляемой Принципалу.
|
||||
|
||||
Отчет о сумме начислений (Отчет) – отчет, формируемый в Личном кабинете Агента на основании данных систем учета Принципала.
|
||||
|
||||
Оферта (Договор) – настоящий документ, который отражает предложение и намерение ООО «БИТФОРС» считать заключенным договор с лицом, которым будет принято предложение на условиях, изложенных ниже.
|
||||
|
||||
|
||||
1. Акцепт оферты и заключение агентского договора
|
||||
-------------------------------------------------
|
||||
|
||||
1.1. Акцепт настоящей Оферты и заключение Агентского договора осуществляется Принципалом в процессе регистрации в Личном кабинете Принципала (на сайте Агента), при прочтении текста настоящей Оферты, путем проставления специальной отметки (галочки) напротив фразы
|
||||
«Я ознакомился с Офертой и принимаю ее условия» и нажатия кнопки «Подписать».
|
||||
|
||||
1.2. Особый порядок принятия условий Оферты путем проставления специальной отметки (галочки) определяется интерфейсом Личного кабинета Принципала. Принципал не может зарегистрироваться в Личном кабинете и получить к нему доступ без подтверждения принятия условий Оферты.
|
||||
|
||||
1.3. Принимая Оферту, Принципал подтверждает, что прочел и полностью согласен с документами, размещенными на сайте в разделе, предназначенном для Принципала, которые являются неотъемлемой частью настоящей Оферты (Договора) и обязательны для исполнения Сторонами.
|
||||
|
||||
|
||||
2. Общие положения
|
||||
------------------
|
||||
|
||||
2.1. Публикуемые на сайте Агента (ссылка на сайт) документы
|
||||
(формы, требования, правила и т.п.), устанавливающие порядок и условия выполнения действий, предусмотренных настоящим Договором, являются неотъемлемой частью настоящего Договора и обязательны для исполнения Сторонами. Принципал обязан использовать формы документов, утвержденных Агентом, и не вправе вносить в них какие-либо изменения или дополнения.
|
||||
|
||||
2.2. Агент обязуется уведомлять Принципала обо всех изменениях в документах, связанных с исполнением настоящего Договора, путем направления электронных сообщений (через Личный кабинет или на электронную почту Принципала) или размещением уведомлений об изменениях на сайте Агентов в разделе, предназначенном для размещения объявлений. Такие сообщения и уведомления приравниваются к сообщениям и уведомлениям, исполненным в простой письменной форме, направляемым на почтовые адреса Принципала.
|
||||
|
||||
2.3. К правам и обязанностям сторон, возникшим на основании настоящего Договора, применяются положения действующей редакции Договора и Приложений, опубликованных на сайте Агента, если иное не установлено Договором.
|
||||
|
||||
|
||||
3. Предмет договора
|
||||
-------------------
|
||||
|
||||
3.1. По настоящему Договору Принципал поручает, а Агент принимает на себя обязательство совершать от имени и за счет Принципала указанные в п. 3.2 настоящего Договора действия, а Принципал обязуется выплатить Агенту вознаграждение за совершенные действия.
|
||||
|
||||
3.2. По настоящему Договору Агент совершает следующие действия:
|
||||
|
||||
– Консультирование Принципала об услугах Агента, включая, помимо прочего, порядок активации и оказания услуг, работу в Личном кабинете Принципала и иные дополнительные услуги, оказываемые Агентом;
|
||||
|
||||
– Совершение сделок и иных юридических действий Агентом от своего имени, но за счёт Принципала.
|
||||
|
||||
3.3. Настоящий Договор действует на территории Российской Федерации и иного иностранного государства.
|
||||
|
||||
3.4. Права и обязанности по сделкам, совершенным Агентом во исполнение настоящего Договора, возникают непосредственно у Принципала.
|
||||
|
||||
3.5. Агент гарантирует отсутствие договорных и иных отношений с лицами, которые могли бы оказать влияние на исполнение настоящего Договора. Агент гарантирует свою независимость и объективность в ходе исполнения настоящего Договора.
|
||||
|
||||
|
||||
4. Права и обязанности сторон
|
||||
-----------------------------
|
||||
|
||||
4.1. Агент обязуется:
|
||||
|
||||
4.1.1. Совершать действия, составляющие предмет настоящего Договора, в соответствии с законными интересами Принципала.
|
||||
|
||||
4.1.2. Сообщать Принципалу по его требованию все сведения о ходе исполнения настоящего Договора.
|
||||
|
||||
4.1.3. Передавать Принципалу в течение 7 (семи) рабочих дней имущество, полученное по сделкам, совершенным во исполнение настоящего Договора.
|
||||
|
||||
4.1.4. Не позднее последнего дня месяца, следующего за отчетным, представлять Принципалу отчет об исполнении поручения с приложением доказательств расходов, произведенных Агентом за счет Принципала.
|
||||
|
||||
4.1.5. Выполнять другие обязанности, которые в соответствии с настоящим Договором или законом возлагаются на Агента.
|
||||
|
||||
4.2. Агент несет ответственность за сохранность документов и персональных данных, переданных ему Принципалом для исполнения настоящего Договора.
|
||||
|
||||
4.3. Агент вправе:
|
||||
|
||||
4.3.1. Отступить от указаний Принципала, если по обстоятельствам дела это необходимо в интересах Принципала и Агент не мог предварительно запросить Принципала либо не получил в течение 3 (трех) рабочих дней ответа на свой запрос. Агент обязан уведомить Принципала о допущенных отступлениях, как только уведомление станет возможным.
|
||||
|
||||
4.3.2. Удержать причитающиеся ему по настоящему Договору суммы вознаграждения из всех сумм, поступивших к нему за счет Принципала.
|
||||
|
||||
4.3.3. Агент вправе заключить субагентский договор с другим лицом. В случае заключения субагентского договора ответственным за действия субагента перед Принципалом остается Агент.
|
||||
|
||||
4.4. Принципал обязан:
|
||||
|
||||
4.4.1. Без промедления принять отчет Агента, все предоставленные им документы и все исполненное им в соответствии с Договором. Принципал, имеющий возражения по отчету Агента, должен сообщить о них Агенту в течение 7 (семи) рабочих дней со дня получения отчета. В противном случае отчет считается принятым Принципалом.
|
||||
|
||||
4.4.2. Обеспечить Агента документами и материалами, необходимыми для выполнения настоящего Договора.
|
||||
|
||||
4.4.3. Возместить Агенту понесенные в связи с исполнением настоящего Договора расходы.
|
||||
|
||||
4.4.4. Выплатить Агенту обусловленное настоящим Договором агентское вознаграждение.
|
||||
|
||||
4.5. Принципал вправе:
|
||||
|
||||
4.5.1. Давать Агенту рекомендации об исполнении настоящего Договора. Указания Принципала должны быть правомерными, осуществимыми и конкретными.
|
||||
|
||||
4.5.2. Получать от Агента сведения о ходе выполнения поручения по требованию.
|
||||
|
||||
4.5.3. Требовать от Агента представления отчета о проделанной работе во исполнение настоящего Договора.
|
||||
|
||||
|
||||
5. Агентское вознаграждение и порядок оплаты
|
||||
--------------------------------------------
|
||||
|
||||
5.1. Сумма вознаграждения Агента по настоящему Договору составляет
|
||||
|
||||
- 8% от 5 000 до 30 000 рублей (вычет процента производится с учетом всех операционных расходов, необходимых для исполнения поручения от своего имени, но за счет Принципала Агентом).
|
||||
|
||||
- 6% от 30 000 до 100 000 рублей (вычет процента производится с учетом всех операционных расходов, необходимых для исполнения поручения от своего имени, но за счет Принципала Агентом).
|
||||
|
||||
- 4% от 100 000 до 600 000 рублей (вычет процента производится с учетом всех операционных расходов, необходимых для исполнения поручения от своего имени, но за счет Принципала Агентом).
|
||||
|
||||
5.2. Вознаграждение выплачивается Агенту в следующем порядке и сроки:
|
||||
|
||||
- Принципал выплачивает Агенту вознаграждение с момента подписания настоящего Договора об исполнении поручения Агентом от своего имени, но за счет Принципала.
|
||||
|
||||
5.3. Принципал возмещает следующие расходы Агента:
|
||||
|
||||
5.3.1. Расходы на оплату банковских услуг и иных комиссий в сумме не более 30 000 рублей.
|
||||
|
||||
5.4. Расходы, указанные в п. 5.3 настоящего Договора, возмещаются Принципалом в следующем порядке:
|
||||
|
||||
- Возмещение расходов Агенту осуществляется в момент подписания настоящего Договора об исполнении поручения Агентом от своего имени, но за счет Принципала.
|
||||
|
||||
5.5. Безналичные расчеты по настоящему Договору производятся Сторонами путем перечисления денежных средств на расчетный счет Стороны по реквизитам, указанным в настоящем Договоре. Размер агентского вознаграждения устанавливается договором между сторонами и выплачивается Агенту за совершение юридических и (или) фактических действий в интересах Принципала.
|
||||
|
||||
|
||||
6. Ответственность сторон
|
||||
-------------------------
|
||||
|
||||
6.1. В случае нарушения Агентом срока, установленного п. 4.1.3 настоящего Договора для передачи Принципалу полученного по настоящему Договору, Принципал вправе предъявить Агенту требование об уплате неустойки в размере 0,1% от непереданной денежной суммы (либо от стоимости непереданного имущества) за каждый день просрочки.
|
||||
|
||||
6.2. В случае нарушения Принципалом срока уплаты вознаграждения, установленного п. 5.2 настоящего Договора, или срока возмещения расходов, установленного п. 5.4 настоящего Договора, Агент вправе предъявить Принципалу требование об уплате неустойки в размере 0,1% от не уплаченной в срок суммы за каждый день просрочки.
|
||||
|
||||
6.3. Ответственность Сторон за неисполнение или ненадлежащее исполнение иных обязательств по настоящему Договору определяется в соответствии с нормами действующего законодательства Российской Федерации.
|
||||
|
||||
|
||||
7. Форс-мажор
|
||||
-------------
|
||||
|
||||
7.1. Стороны освобождаются от ответственности за частичное или полное неисполнение обязательств по настоящему Договору, если это неисполнение явилось следствием возникших после заключения настоящего Договора обстоятельств непреодолимой силы, которые Стороны не могли предвидеть или предотвратить.
|
||||
|
||||
7.2. При наступлении обстоятельств, указанных в п. 7.1 настоящего Договора, каждая Сторона должна без промедления известить о них в письменном виде другую Сторону. Извещение должно содержать данные о характере обстоятельств, а также официальные документы, удостоверяющие наличие этих обстоятельств и, по возможности, дающие оценку их влияния на исполнение Стороной своих обязательств по настоящему Договору.
|
||||
|
||||
7.3. Если Сторона не направит или несвоевременно направит извещение, предусмотренное в п. 7.2 настоящего Договора, то она обязана возместить второй Стороне понесенные ею убытки.
|
||||
|
||||
7.4. В случаях наступления обстоятельств, предусмотренных в п. 7.1 настоящего Договора, срок выполнения Стороной обязательств по настоящему Договору отодвигается соразмерно времени, в течение которого действуют эти обстоятельства и их последствия.
|
||||
|
||||
7.5. Если обстоятельства, указанные в п. 7.1 настоящего Договора, и их последствия продолжают действовать более трех недель:
|
||||
|
||||
- Стороны проводят дополнительные переговоры для выявления приемлемых альтернативных способов исполнения настоящего Договора.
|
||||
|
||||
- Сторона, не затронутая ее действием, вправе расторгнуть Договор в одностороннем порядке, направив другой Стороне соответствующее извещение и не возмещая каких-либо убытков, вызванных расторжением Договора.
|
||||
|
||||
|
||||
8. Конфиденциальность
|
||||
---------------------
|
||||
|
||||
8.1. Стороны принимают все необходимые меры для того, чтобы их сотрудники, агенты, правопреемники без предварительного согласия другой Стороны не информировали третьих лиц о конфиденциальной информации и персональных данных Сторон настоящего Договора.
|
||||
|
||||
|
||||
9. Изменение и прекращение договора
|
||||
-----------------------------------
|
||||
|
||||
9.1. Настоящий договор вступает в силу с момента его подписания и действует до момента исполнения сторонами своих обязательств по настоящему договору.
|
||||
|
||||
9.2. Настоящий Договор может быть изменен или прекращен по письменному соглашению Сторон, а также в других случаях, предусмотренных законодательством Российской Федерации.
|
||||
|
||||
9.3. Принципал вправе в любое время отказаться от исполнения настоящего Договора путем направления письменного уведомления Агенту за 3 (три) рабочих дня.
|
||||
|
||||
9.4. В случае отказа от настоящего Договора Принципал обязан незамедлительно после направления уведомления Агенту распорядиться своим имуществом, находящимся в ведении Агента, и не позднее 7 (семи) рабочих дней произвести выплату причитающегося Агенту вознаграждения за действия, совершенные им до прекращения Договора.
|
||||
|
||||
9.5. Агент вправе отказаться от исполнения настоящего Договора путем направления письменного уведомления Принципалу во всякое время.
|
||||
|
||||
9.6. Агент обязан принять меры, необходимые для обеспечения сохранности имущества Принципала. Принципал должен незамедлительно распорядиться своим находящимся в ведении Агента имуществом.
|
||||
|
||||
9.7. Агент, отказавшийся от настоящего Договора, сохраняет право на вознаграждение за действия, выполненные им до прекращения Договора.
|
||||
|
||||
|
||||
10. Заключительные положения
|
||||
----------------------------
|
||||
|
||||
10.1. Ни одна из сторон не вправе передавать свои права и обязанности по настоящему договору третьим лицам без согласия другой стороны.
|
||||
|
||||
10.2. Если иное не предусмотрено Договором, извещения, уведомления, требования и иные юридически значимые сообщения (далее - сообщения) Стороны могут направлять по факсу, электронной почте или другим способом связи при условии, что он позволяет достоверно установить, от кого исходило сообщение и кому оно адресовано.
|
||||
|
||||
10.3. Сообщения влекут гражданско-правовые последствия для Стороны, которой направлены, с момента их доставки указанной Стороне или ее представителю. Такие последствия возникают и в случае, когда сообщение не было вручено адресату по зависящим от него обстоятельствам
|
||||
(п. 1 ст. 165.1 Гражданский Кодекс Российской Федерации).
|
||||
|
||||
10.4. Во всем остальном, что не предусмотрено настоящим Договором, Стороны руководствуются действующим законодательством Российской Федерации.
|
||||
|
||||
10.5. Споры, вытекающие из настоящего Договора, разрешаются в досудебном порядке. При неурегулировании возникших разногласий спор разрешается в Арбитражном суде г. Санкт–Петербурга и Ленинградской области с обязательным соблюдением претензионного порядка. Срок ответа на претензию составляет 14 (четырнадцать) рабочих дней.
|
||||
|
||||
|
||||
Реквизиты сторон
|
||||
----------------
|
||||
|
||||
Общество с ограниченной ответственностью «БИТФОРС»
|
||||
|
||||
196246, г. Санкт-Петербург, Московский р-н, Московское шоссе, д.25к1 литера в, помещ. 3-Н
|
||||
|
||||
ИНН / КПП 9810001062 / 781001001
|
||||
|
||||
ОГРН 1257800060990
|
||||
|
||||
ОКПО / ОКАТО / ОКТМО 68342261 / 40284000000 / 40377000000
|
||||
|
||||
Руководитель: Кленин Михаил Васильевич
|
||||
|
||||
Электронная почта: company@bitforcefoundation.ru
|
||||
|
||||
Наименование банка: ФИЛИАЛ "САНКТ-ПЕТЕРБУРГСКИЙ" АО "АЛЬФА-БАНК"
|
||||
|
||||
Корреспондентский счет 30101810600000000786
|
||||
|
||||
БИК 044030786
|
||||
|
||||
Расчетный счет 40702810632250004861
|
||||
4
reestr-operatora-pd-rkn.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
Реестр операторов персональных данных (Роскомнадзор)
|
||||
ООО «БИТФОРС»
|
||||
|
||||
https://pd.rkn.gov.ru/operators-registry/operators-list/?act=search&name_full=%D0%91%D0%B8%D1%82%D1%84%D0%BE%D1%80%D1%81&inn=9810001062®n=
|
||||
337
soglasie-na-obrabotku-personalnyh-dannyh.txt
Normal file
@@ -0,0 +1,337 @@
|
||||
СОГЛАСИЕ НА ОБРАБОТКУ ПЕРСОНАЛЬНЫХ ДАННЫХ
|
||||
=========================================
|
||||
|
||||
ООО «БИТФОРС»
|
||||
|
||||
Преамбула
|
||||
---------
|
||||
|
||||
Я, субъект персональных данных, действуя своей волей и в своем интересе, в соответствии с требованиями Федерального закона от 27.07.2006 № 152-ФЗ «О персональных данных» (далее — Закон), предоставляю ООО «БИТФОРС» (далее — Оператор, Общество) согласие на обработку моих персональных данных на условиях и для целей, определенных настоящим Согласием.
|
||||
|
||||
|
||||
1. Сведения об операторе
|
||||
------------------------
|
||||
|
||||
Полное наименование: Общество с ограниченной ответственностью «БИТФОРС»
|
||||
|
||||
ИНН: 9810001062
|
||||
|
||||
ОГРН: 1257800060990
|
||||
|
||||
Реестр ОПД: Посмотреть в реестре Роскомнадзора (https://pd.rkn.gov.ru/operators-registry/operators-list/?act=search&name_full=%D0%91%D0%B8%D1%82%D1%84%D0%BE%D1%80%D1%81&inn=9810001062®n=)
|
||||
|
||||
Юридический адрес: 196246, город Санкт-Петербург, Московское шоссе, дом 25, корпус 1, литера В, помещение 3-н
|
||||
|
||||
Контактная информация:
|
||||
• Электронная почта: company@bitforcefoundation.ru
|
||||
• Веб-сайт: https://bitforce-foundation.ru
|
||||
|
||||
|
||||
2. Правовые основания обработки
|
||||
-------------------------------
|
||||
|
||||
2.1. Настоящее согласие предоставляется на основании пункта 1 части 1 статьи 6 Федерального закона «О персональных данных» и является правовым основанием для обработки персональных данных Оператором.
|
||||
|
||||
2.2. Согласие дается добровольно, своей волей и в своих интересах.
|
||||
|
||||
2.3. Субъект персональных данных понимает последствия предоставления согласия, включая возможные риски, связанные с обработкой персональных данных.
|
||||
|
||||
|
||||
3. Цели обработки персональных данных
|
||||
-------------------------------------
|
||||
|
||||
Согласие предоставляется для обработки персональных данных в следующих целях:
|
||||
|
||||
3.1. Основные цели:
|
||||
• Регистрация и ведение учетной записи на веб-сайте https://bitforce-foundation.ru и в мобильном приложении;
|
||||
• Идентификация и верификация личности в соответствии с требованиями законодательства Российской Федерации;
|
||||
• Предоставление услуг по обмену криптовалют и электронных денежных средств;
|
||||
• Проведение финансовых операций, переводов и расчетов;
|
||||
• Ведение учета и истории операций.
|
||||
|
||||
3.2. Дополнительные цели:
|
||||
• Обеспечение безопасности операций и предотвращение мошенничества;
|
||||
• Выполнение требований по противодействию легализации доходов, полученных преступным путем, и финансированию терроризма;
|
||||
• Соблюдение требований валютного, налогового и иного применимого законодательства;
|
||||
• Предоставление технической поддержки и клиентского сервиса;
|
||||
• Рассылка уведомлений о состоянии операций и изменениях в условиях предоставления услуг.
|
||||
|
||||
3.3. Маркетинговые цели (при дополнительном согласии):
|
||||
• Направление информационных и рекламных материалов;
|
||||
• Проведение маркетинговых исследований и опросов;
|
||||
• Персонализация предложений и услуг;
|
||||
• Анализ предпочтений и поведения для улучшения сервисов.
|
||||
|
||||
3.4. Аналитические цели:
|
||||
• Анализ использования веб-сайта и мобильного приложения;
|
||||
• Улучшение качества предоставляемых услуг;
|
||||
• Разработка новых продуктов и сервисов;
|
||||
• Создание статистических отчетов в обезличенном виде.
|
||||
|
||||
|
||||
4. Перечень персональных данных, на обработку которых дается согласие
|
||||
------------------------------------------------------------
|
||||
|
||||
4.1. Идентификационные данные:
|
||||
• Фамилия, имя, отчество;
|
||||
• Дата рождения;
|
||||
• Место рождения;
|
||||
• Гражданство;
|
||||
• Пол.
|
||||
|
||||
4.2. Документы, удостоверяющие личность:
|
||||
• Серия и номер паспорта гражданина Российской Федерации;
|
||||
• Дата выдачи и код подразделения;
|
||||
• Наименование органа, выдавшего документ;
|
||||
• Адрес регистрации по месту жительства;
|
||||
• Цифровые копии (сканы) документов.
|
||||
|
||||
4.3. Контактная информация:
|
||||
• Номера телефонов (мобильный, домашний, рабочий);
|
||||
• Адреса электронной почты;
|
||||
• Почтовые адреса (фактического проживания, для корреспонденции);
|
||||
• Данные мессенджеров и социальных сетей (при предоставлении).
|
||||
|
||||
4.4. Финансовая информация:
|
||||
• Номера банковских счетов и реквизиты банковских карт;
|
||||
• Реквизиты криптовалютных кошельков и криптовалютных адресов;
|
||||
• Информация о доходах и источниках происхождения денежных средств;
|
||||
• История финансовых операций и транзакций;
|
||||
• Данные о налоговом статусе и резидентстве.
|
||||
|
||||
4.5. Техническая информация:
|
||||
• IP-адреса устройств, с которых осуществляется доступ к сервисам;
|
||||
• Информация о браузере, операционной системе и устройстве;
|
||||
• Файлы cookie и данные локального хранилища;
|
||||
• Логи действий и история использования сервисов;
|
||||
• Геолокационные данные (при включенной функции).
|
||||
|
||||
4.6. Дополнительная информация:
|
||||
• Фотографии для процедур верификации;
|
||||
• Видеозаписи процедур видеоидентификации;
|
||||
• Биометрические данные (при использовании соответствующих технологий);
|
||||
• Информация о семейном положении и близких родственниках (при необходимости);
|
||||
• Сведения о профессиональной деятельности и должности;
|
||||
• Любая иная информация, предоставленная субъектом добровольно.
|
||||
|
||||
4.7. Специальные категории персональных данных:
|
||||
При необходимости и при наличии отдельного письменного согласия:
|
||||
• Биометрические персональные данные;
|
||||
• Данные о состоянии здоровья (при оформлении страхования);
|
||||
• Данные о судимости (при проверках безопасности).
|
||||
|
||||
|
||||
5. Перечень действий с персональными данными
|
||||
--------------------------------------------
|
||||
|
||||
Согласие распространяется на следующие действия (операции) с персональными данными:
|
||||
|
||||
5.1. Действия по получению и первичной обработке:
|
||||
• Сбор персональных данных;
|
||||
• Запись на материальные и электронные носители;
|
||||
• Первичная систематизация и структурирование;
|
||||
• Проверка достоверности и полноты данных.
|
||||
|
||||
5.2. Действия по хранению и систематизации:
|
||||
• Накопление персональных данных в базах данных;
|
||||
• Систематизация и каталогизация;
|
||||
• Создание резервных копий;
|
||||
• Архивирование данных.
|
||||
|
||||
5.3. Действия по использованию и анализу:
|
||||
• Извлечение персональных данных из баз данных;
|
||||
• Использование для достижения заявленных целей;
|
||||
• Анализ и обработка для получения аналитической информации;
|
||||
• Сопоставление с данными из других источников.
|
||||
|
||||
5.4. Действия по изменению и актуализации:
|
||||
• Уточнение (обновление, изменение) персональных данных;
|
||||
• Дополнение новой информацией;
|
||||
• Исправление выявленных неточностей;
|
||||
• Актуализация устаревших данных.
|
||||
|
||||
5.5. Действия по передаче:
|
||||
• Передача персональных данных третьим лицам;
|
||||
• Предоставление доступа уполномоченным лицам;
|
||||
|
||||
5.6. Действия по обезличиванию и уничтожению:
|
||||
• Обезличивание персональных данных;
|
||||
• Блокирование доступа к персональным данным;
|
||||
• Удаление персональных данных;
|
||||
• Уничтожение носителей персональных данных.
|
||||
|
||||
5.7. Автоматизированная обработка:
|
||||
• Автоматический сбор данных через веб-сайт и приложения;
|
||||
• Автоматизированный анализ и принятие решений;
|
||||
• Машинное обучение и использование алгоритмов;
|
||||
• Профилирование и сегментация.
|
||||
|
||||
|
||||
6. Лица, которым могут быть переданы персональные данные
|
||||
--------------------------------------------------------
|
||||
|
||||
6.1. Сотрудники Оператора:
|
||||
• Уполномоченные сотрудники, непосредственно участвующие в обработке персональных данных;
|
||||
• Сотрудники службы безопасности и комплаенса;
|
||||
• Сотрудники технической поддержки;
|
||||
• Руководящий состав в рамках их полномочий.
|
||||
|
||||
6.2. Государственные и муниципальные органы:
|
||||
• Федеральная служба по финансовому мониторингу (Росфинмониторинг);
|
||||
• Федеральная налоговая служба;
|
||||
• Правоохранительные органы (при наличии законных требований);
|
||||
• Суды и органы исполнения судебных решений;
|
||||
• Иные государственные органы в рамках их компетенции.
|
||||
|
||||
6.3. Партнеры и контрагенты:
|
||||
• Банки;
|
||||
• Платежные системы;
|
||||
• Операторы электронных денежных средств;
|
||||
• Поставщики технологических решений;
|
||||
• Аудиторские и консалтинговые организации.
|
||||
|
||||
6.4. Третьи лица для специальных целей:
|
||||
• Службы доставки и курьерские службы;
|
||||
• Телекоммуникационные операторы;
|
||||
• Маркетинговые агентства (при согласии на маркетинг);
|
||||
• Сервисы аналитики и веб-метрики;
|
||||
• Облачные провайдеры и хостинг-провайдеры;
|
||||
• Архивные организации.
|
||||
|
||||
6.5. Условия передачи:
|
||||
• Передача осуществляется только для целей, указанных в настоящем согласии;
|
||||
• Получатели обязуются обеспечить конфиденциальность и безопасность данных;
|
||||
• Заключаются соответствующие соглашения о защите персональных данных.
|
||||
|
||||
|
||||
7. Сроки обработки персональных данных
|
||||
--------------------------------------
|
||||
|
||||
7.1. Общие принципы определения сроков:
|
||||
• Персональные данные обрабатываются в течение времени, необходимого для достижения целей обработки;
|
||||
• Сроки определяются требованиями законодательства и характером отношений с Оператором;
|
||||
• После достижения целей обработки данные подлежат уничтожению или обезличиванию.
|
||||
|
||||
7.2. Конкретные сроки обработки:
|
||||
|
||||
Данные активных клиентов:
|
||||
• В течение всего периода действия отношений с Оператором;
|
||||
• Плюс 5 лет после прекращения отношений (в соответствии с требованиями валютного законодательства);
|
||||
• Плюс дополнительные сроки архивного хранения согласно законодательству.
|
||||
|
||||
Данные для идентификации и верификации:
|
||||
• 5 лет с момента прекращения отношений с клиентом;
|
||||
• 5 лет с даты проведения последней операции;
|
||||
• До истечения сроков исковой давности по спорным операциям.
|
||||
|
||||
Финансовая информация и история операций:
|
||||
• 5 лет с даты совершения операции (требования по ПОД/ФТ);
|
||||
• 5 лет для налогового учета;
|
||||
• До завершения всех расследований и судебных процедур.
|
||||
|
||||
Маркетинговые данные:
|
||||
• До отзыва согласия на маркетинговые коммуникации;
|
||||
• Не более 3 лет с момента последнего взаимодействия;
|
||||
• Немедленное удаление при отказе от рассылок.
|
||||
|
||||
Техническая информация (логи, IP-адреса):
|
||||
• 1 год для обеспечения информационной безопасности;
|
||||
• 6 месяцев для технических логов;
|
||||
• Постоянно в обезличенном виде для статистики.
|
||||
|
||||
7.3. Досрочное прекращение обработки:
|
||||
• При отзыве согласия субъектом персональных данных;
|
||||
• При выявлении незаконности получения или обработки;
|
||||
• По требованию уполномоченных органов;
|
||||
• При ликвидации Оператора.
|
||||
|
||||
|
||||
8. Права субъекта персональных данных
|
||||
-------------------------------------
|
||||
|
||||
8.1. Основные права:
|
||||
|
||||
Право на информацию:
|
||||
• Получение подтверждения факта обработки персональных данных;
|
||||
• Получение информации о целях, правовых основаниях и способах обработки;
|
||||
• Получение сведений о сроках обработки и составе данных;
|
||||
• Информация о третьих лицах, которым передаются данные.
|
||||
|
||||
Право на доступ:
|
||||
• Получение копий обрабатываемых персональных данных;
|
||||
• Ознакомление с историей обработки и изменений;
|
||||
• Получение информации об источниках персональных данных;
|
||||
• Доступ к автоматизированным решениям, принятым на основе данных.
|
||||
|
||||
Право на исправление:
|
||||
• Требование исправления неточных или неполных данных;
|
||||
• Дополнение недостающей информации;
|
||||
• Актуализация устаревших данных;
|
||||
• Получение подтверждения о внесенных изменениях.
|
||||
|
||||
Право на удаление ("право на забвение"):
|
||||
• Требование удаления персональных данных при наличии оснований;
|
||||
• Удаление данных после отзыва согласия;
|
||||
• Удаление при прекращении правовых оснований для обработки;
|
||||
• Получение подтверждения об удалении.
|
||||
|
||||
Право на ограничение обработки:
|
||||
• Требование блокирования обработки на время проверки точности данных;
|
||||
• Ограничение способов обработки;
|
||||
• Приостановление передачи третьим лицам;
|
||||
• Сохранение данных без их активного использования.
|
||||
|
||||
8.2. Право на отзыв согласия:
|
||||
• Согласие может быть отозвано в любое время;
|
||||
• Отзыв не влияет на законность обработки до момента отзыва;
|
||||
• Отзыв оформляется в письменной форме;
|
||||
• После отзыва обработка прекращается в разумные сроки.
|
||||
|
||||
8.3. Право на обжалование:
|
||||
• Обращение к Оператору с жалобами на действия по обработке данных;
|
||||
• Обращение в Роскомнадзор или его территориальные органы;
|
||||
• Обращение в суд для защиты нарушенных прав;
|
||||
• Требование возмещения морального и материального вреда.
|
||||
|
||||
8.4. Порядок реализации прав:
|
||||
• Обращения направляются на адрес: company@bitforcefoundation.ru;
|
||||
• Обращения рассматриваются в течение 30 дней;
|
||||
• При необходимости срок может быть продлен на 30 дней;
|
||||
• Ответ предоставляется в письменной или электронной форме по выбору субъекта.
|
||||
|
||||
|
||||
9. Заключительные положения
|
||||
---------------------------
|
||||
|
||||
9.1. Действие согласия:
|
||||
• Согласие действует с момента его предоставления;
|
||||
• Согласие действует до его отзыва или до достижения целей обработки;
|
||||
• Согласие может быть изменено по взаимному соглашению сторон;
|
||||
• При существенных изменениях целей требуется новое согласие.
|
||||
|
||||
9.2. Форма предоставления согласия:
|
||||
• Согласие может быть предоставлено в письменной форме;
|
||||
• Согласие может быть предоставлено в электронной форме;
|
||||
• Согласие может выражаться путем совершения конклюдентных действий;
|
||||
• Согласие фиксируется и сохраняется Оператором.
|
||||
|
||||
9.3. Последствия непредоставления согласия:
|
||||
• Отказ в предоставлении согласия может повлечь невозможность регистрации;
|
||||
• Отказ может ограничить доступ к отдельным услугам;
|
||||
• Отказ в согласии на маркетинг не влияет на основные услуги;
|
||||
• Субъект вправе предоставить частичное согласие.
|
||||
|
||||
9.4. Контактная информация:
|
||||
Для реализации прав и направления обращений:
|
||||
• Почтовый адрес: 196246, г. Санкт-Петербург, Московское ш., д. 25, к. 1, лит. В, пом. 3-н
|
||||
• Электронная почта: company@bitforcefoundation.ru
|
||||
• Ответственное лицо: Кленин Михаил Васильевич
|
||||
• Официальный сайт: https://bitforce-foundation.ru
|
||||
|
||||
9.5. Подтверждение понимания:
|
||||
Предоставляя настоящее согласие, я подтверждаю, что:
|
||||
• Ознакомлен с содержанием согласия и понимаю его значение;
|
||||
• Понимаю цели и способы обработки моих персональных данных;
|
||||
• Знаю о своих правах и способах их реализации;
|
||||
• Согласие предоставляется добровольно и осознанно;
|
||||
• Имею возможность отозвать согласие в любое время.
|
||||
@@ -1,175 +0,0 @@
|
||||
<!DOCTYPE html><html lang="ru"><head>
|
||||
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>ЭКСА — Сид Фраза</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
background: #0A0B2E;
|
||||
color: #FFFFFF;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
|
||||
min-height: 100vh;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
/* Navbar */
|
||||
.nav{display:flex;align-items:center;justify-content:space-between;padding:0 32px;height:60px;border-bottom:1px solid rgba(255,255,255,0.06);flex-shrink:0}
|
||||
.nav-logo{display:flex;align-items:center}
|
||||
.nav-logo img{height:32px}
|
||||
.ticker{display:flex;gap:24px;font-size:13px;font-family:var(--mono)}
|
||||
.tick{display:flex;align-items:center;gap:6px;color:#B5B0CC}
|
||||
.tick b{color:#fff}
|
||||
.tick .up{color:#00C48C}.tick .dn{color:#FF4D4D}
|
||||
.nav-account{display:flex;align-items:center;gap:10px}
|
||||
.avatar{width:34px;height:34px;border-radius:50%;background:#3D2A8E}
|
||||
.nav-account span{color:#B5B0CC;font-size:14px;font-weight:500}
|
||||
|
||||
/* Content */
|
||||
.content {
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 40px 32px 60px;
|
||||
}
|
||||
|
||||
/* Title row */
|
||||
.title-row {
|
||||
display: flex; align-items: flex-start; justify-content: space-between;
|
||||
}
|
||||
.title-row h1 {
|
||||
font-size: 20px; font-weight: 700; letter-spacing: 0.04em;
|
||||
}
|
||||
.title-buttons {
|
||||
display: flex; flex-direction: column; gap: 8px; align-items: flex-end;
|
||||
}
|
||||
.btn-outline {
|
||||
background: rgba(255,255,255,0.08);
|
||||
border: 1px solid rgba(255,255,255,0.1);
|
||||
color: #fff;
|
||||
border-radius: 10px;
|
||||
width: 160px;
|
||||
padding: 10px 0;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.06em;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s, border-color 0.2s;
|
||||
text-align: center;
|
||||
}
|
||||
.btn-outline:hover {
|
||||
background: rgba(255,255,255,0.12);
|
||||
border-color: rgba(255,255,255,0.18);
|
||||
}
|
||||
|
||||
/* Subtitle */
|
||||
.subtitle {
|
||||
margin-top: 12px;
|
||||
font-size: 12px;
|
||||
color: #B5B0CC;
|
||||
font-variant: all-small-caps;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
.subtitle .countdown { color: #4A6DFF; font-weight: 700; }
|
||||
|
||||
/* Grid */
|
||||
.seed-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 16px;
|
||||
margin-top: 32px;
|
||||
}
|
||||
.seed-card {
|
||||
background: rgba(255,255,255,0.04);
|
||||
border: 1px solid rgba(255,255,255,0.08);
|
||||
border-radius: 14px;
|
||||
height: 52px;
|
||||
display: flex; align-items: center;
|
||||
padding: 0 18px;
|
||||
gap: 10px;
|
||||
transition: border-color 0.25s, box-shadow 0.25s;
|
||||
cursor: default;
|
||||
user-select: none;
|
||||
}
|
||||
.seed-card:hover {
|
||||
border-color: rgba(74,109,255,0.4);
|
||||
box-shadow: 0 0 12px rgba(74,109,255,0.15);
|
||||
}
|
||||
.seed-num {
|
||||
color: #B5B0CC;
|
||||
font-size: 13px;
|
||||
min-width: 22px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.seed-word {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Warning */
|
||||
.warning {
|
||||
margin-top: 32px;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.warning p {
|
||||
max-width: 480px;
|
||||
font-size: 13px;
|
||||
color: #B5B0CC;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.warning .icon { color: #FF4D4D; font-size: 18px; margin-bottom: 8px; }
|
||||
</style>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
|
||||
<style>:root{--mono:'JetBrains Mono',monospace}</style></head>
|
||||
<body data-cc-id="cc-4" style="cursor: crosshair; font-family: Manrope;">
|
||||
|
||||
<nav class="nav">
|
||||
<div class="nav-logo">
|
||||
<img src="logo-full-white.png" alt="ЭКСА">
|
||||
</div>
|
||||
<div class="ticker">
|
||||
<div class="tick"><b>BTC</b> $66,916.00 <span class="up">+0.12%</span></div>
|
||||
<div class="tick"><b>ETH</b> $2,053.97 <span class="dn">−0.12%</span></div>
|
||||
<div class="tick"><b>SOL</b> $163.84 <span class="dn">−1.57%</span></div>
|
||||
</div>
|
||||
<div class="nav-account">
|
||||
<div class="avatar"></div>
|
||||
<span>Account 1</span>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="content" style="font-family: Manrope" data-cc-id="cc-5">
|
||||
<div class="title-row" style="height: 70px; width: 896px" data-cc-id="cc-16">
|
||||
<h1 style="width: 250px; font-size: 32px; font-family: Manrope;" data-cc-id="cc-17">СИД ФРАЗА</h1>
|
||||
<div class="title-buttons">
|
||||
<button class="btn-outline">СКРЫТЬ</button>
|
||||
<button class="btn-outline" style="font-size: 13px">КОПИРОВАТЬ</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="subtitle" style="font-size: 14px" data-cc-id="cc-15">АВТОМАТИЧЕСКОЕ СКРЫТИЕ ЧЕРЕЗ <span class="countdown" id="countdown">14</span>С</div>
|
||||
|
||||
<div class="seed-grid" id="seedGrid" data-cc-id="cc-9"><div class="seed-card"><span class="seed-num">1.</span><span class="seed-word">egg</span></div><div class="seed-card" data-cc-id="cc-13"><span class="seed-num">2.</span><span class="seed-word" data-cc-id="cc-14">phone</span></div><div class="seed-card"><span class="seed-num">3.</span><span class="seed-word">long</span></div><div class="seed-card"><span class="seed-num">4.</span><span class="seed-word">vibe</span></div><div class="seed-card" data-cc-id="cc-11"><span class="seed-num">5.</span><span class="seed-word" data-cc-id="cc-12">potato</span></div><div class="seed-card"><span class="seed-num">6.</span><span class="seed-word">soup</span></div><div class="seed-card" data-cc-id="cc-7"><span class="seed-num">7.</span><span class="seed-word" data-cc-id="cc-8">skirt</span></div><div class="seed-card" data-cc-id="cc-10"><span class="seed-num">8.</span><span class="seed-word">black</span></div><div class="seed-card"><span class="seed-num">9.</span><span class="seed-word">phase</span></div><div class="seed-card" data-cc-id="cc-6"><span class="seed-num">10.</span><span class="seed-word">word</span></div><div class="seed-card"><span class="seed-num">11.</span><span class="seed-word">num</span></div><div class="seed-card"><span class="seed-num">12.</span><span class="seed-word">cucumber</span></div></div>
|
||||
|
||||
<div class="warning" style="justify-content: center; flex-direction: row; align-items: flex-start">
|
||||
<div class="icon" style="padding: 16px">⚠️</div>
|
||||
<p>Никогда не передавайте сид-фразу третьим лицам. Тот, кто знает фразу — владеет кошельком.</p>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
let sec = 52;
|
||||
const el = document.getElementById('countdown');
|
||||
setInterval(() => {
|
||||
if (sec > 0) { sec--; el.textContent = sec; }
|
||||
}, 1000);
|
||||
</script>
|
||||
|
||||
|
||||
</body></html>
|
||||
@@ -1,11 +1,13 @@
|
||||
import { Navigate, Outlet } from 'react-router-dom'
|
||||
import { Navigate, Outlet, useLocation } from 'react-router-dom'
|
||||
import { useIsAuthenticated } from '@features/auth'
|
||||
import { ROUTES } from '@shared/config/routes'
|
||||
|
||||
export function GuestRoute() {
|
||||
const { isAuthenticated, isLoading } = useIsAuthenticated()
|
||||
const location = useLocation()
|
||||
const from = (location.state as { from?: { pathname: string } })?.from?.pathname ?? ROUTES.WALLET
|
||||
|
||||
if (isLoading) return null
|
||||
if (isAuthenticated) return <Navigate to={ROUTES.WALLET} replace />
|
||||
if (isAuthenticated) return <Navigate to={from} replace />
|
||||
return <Outlet />
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
import type { ReactNode } from 'react'
|
||||
|
||||
const queryClient = new QueryClient()
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: { queries: { retry: false } },
|
||||
})
|
||||
|
||||
export function QueryProvider({ children }: { children: ReactNode }) {
|
||||
return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
||||
|
||||
@@ -2,12 +2,23 @@ import { BrowserRouter, Route, Routes } from 'react-router-dom'
|
||||
import { HomePage } from '@pages/home'
|
||||
import { WalletPage } from '@pages/wallet'
|
||||
import { SwapPage } from '@pages/swap'
|
||||
import { BridgePage } from '@pages/bridge'
|
||||
import { ProfilePage } from '@pages/profile'
|
||||
import { LoginPage } from '@pages/login'
|
||||
import { RegisterPage } from '@pages/register'
|
||||
import { RegisterTestPage } from '@pages/register-test'
|
||||
import { ConverterTestPage } from '@pages/converter-test'
|
||||
import { ConverterPage } from '@pages/converter'
|
||||
import { SeedPhrasePage } from '@pages/seed-phrase'
|
||||
import { KycPage } from '@pages/kyc'
|
||||
import { RestorePasswordPage } from '@pages/restore-password'
|
||||
import { PublichnayaOfertaPage } from '@pages/publichnaya-oferta'
|
||||
import { PolitikaPage } from '@pages/politika-personalnyh-dannyh'
|
||||
import { PolitikaCookiePage } from '@pages/politika-cookie'
|
||||
import { SoglasiePage } from '@pages/soglasie-personalnyh-dannyh'
|
||||
import { ReestryPage } from '@pages/reestr-pd-rkn'
|
||||
import { TransactionsPage } from '@pages/transactions'
|
||||
import { WalletLayout } from '@widgets/wallet-layout'
|
||||
import { ROUTES } from '@shared/config/routes'
|
||||
import { ScrollToTop } from './ScrollToTop'
|
||||
import { ProtectedRoute } from './ProtectedRoute'
|
||||
@@ -19,21 +30,37 @@ export function RouterProvider() {
|
||||
<ScrollToTop />
|
||||
<Routes>
|
||||
<Route path={ROUTES.HOME} element={<HomePage />} />
|
||||
<Route path={ROUTES.PUBLICHNAYA_OFERTA} element={<PublichnayaOfertaPage />} />
|
||||
<Route path={ROUTES.POLITIKA_PERSONALNYH_DANNYH} element={<PolitikaPage />} />
|
||||
<Route path={ROUTES.POLITIKA_COOKIE} element={<PolitikaCookiePage />} />
|
||||
<Route path={ROUTES.SOGLASIE_PERSONALNYH_DANNYH} element={<SoglasiePage />} />
|
||||
<Route path={ROUTES.REESTR_PD_RKN} element={<ReestryPage />} />
|
||||
<Route path={ROUTES.REGISTER_TEST} element={<RegisterTestPage />} />
|
||||
<Route path={ROUTES.CONVERTER_TEST} element={<ConverterTestPage />} />
|
||||
|
||||
<Route element={<GuestRoute />}>
|
||||
<Route path={ROUTES.LOGIN} element={<LoginPage />} />
|
||||
<Route path={ROUTES.REGISTER} element={<RegisterPage />} />
|
||||
<Route path={ROUTES.RESTORE_PASSWORD} element={<RestorePasswordPage />} />
|
||||
</Route>
|
||||
|
||||
<Route path={ROUTES.CONVERTER} element={<ConverterPage />} />
|
||||
|
||||
<Route element={<ProtectedRoute />}>
|
||||
<Route element={<WalletLayout footer center />}>
|
||||
<Route path={ROUTES.CONVERTER} element={<ConverterPage />} />
|
||||
</Route>
|
||||
|
||||
<Route element={<WalletLayout footer />}>
|
||||
<Route path={ROUTES.SWAP} element={<SwapPage />} />
|
||||
<Route path={ROUTES.BRIDGE} element={<BridgePage />} />
|
||||
<Route path={ROUTES.TRANSACTIONS} element={<TransactionsPage />} />
|
||||
</Route>
|
||||
|
||||
<Route path={ROUTES.WALLET} element={<WalletPage />} />
|
||||
<Route path={ROUTES.SWAP} element={<SwapPage />} />
|
||||
<Route path={ROUTES.WALLET_CHAIN} element={<WalletPage />} />
|
||||
<Route path={ROUTES.PROFILE} element={<ProfilePage />} />
|
||||
<Route path={ROUTES.SEED_PHRASE} element={<SeedPhrasePage />} />
|
||||
<Route path={ROUTES.KYC} element={<KycPage />} />
|
||||
</Route>
|
||||
<Route path={ROUTES.KYC} element={<KycPage />} />
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
)
|
||||
|
||||
@@ -5,8 +5,28 @@ body {
|
||||
overflow-x: hidden;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
scrollbar-color: var(--grad-center) var(--bg-mid);
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
#root {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: var(--bg-mid);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--grad-center);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--highlight);
|
||||
}
|
||||
|
||||
896
src/b2bapi.json
Normal file
@@ -0,0 +1,896 @@
|
||||
{
|
||||
"openapi": "3.1.0",
|
||||
"info": {
|
||||
"title": "B2B Service",
|
||||
"description": "Purchase requests API for legal entity client users.",
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
"url": "https://opensource.org/licenses/MIT"
|
||||
},
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"paths": {
|
||||
"/v1/organizations": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"organizations"
|
||||
],
|
||||
"summary": "Get Organization",
|
||||
"operationId": "get_organization_v1_organizations_get",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/OrganizationResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/organizations/wallets": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"organizations"
|
||||
],
|
||||
"summary": "List Organization Wallets",
|
||||
"operationId": "list_organization_wallets_v1_organizations_wallets_get",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/WalletResponse"
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Response List Organization Wallets V1 Organizations Wallets Get"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/organizations/wallets/mnemonic": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"organizations"
|
||||
],
|
||||
"summary": "Get Organization Mnemonic",
|
||||
"operationId": "get_organization_mnemonic_v1_organizations_wallets_mnemonic_get",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/MnemonicResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/organizations/wallets/secret-keys": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"organizations"
|
||||
],
|
||||
"summary": "Get Organization Secret Keys",
|
||||
"operationId": "get_organization_secret_keys_v1_organizations_wallets_secret_keys_get",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/SecretKeyResponse"
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Response Get Organization Secret Keys V1 Organizations Wallets Secret Keys Get"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/purchase-requests": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"purchase-requests"
|
||||
],
|
||||
"summary": "Create Purchase Request",
|
||||
"operationId": "create_purchase_request_purchase_requests_post",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "X-CSRF-Token",
|
||||
"in": "header",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "X-Csrf-Token"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CreatePurchaseRequestBody"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/PurchaseRequestResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"get": {
|
||||
"tags": [
|
||||
"purchase-requests"
|
||||
],
|
||||
"summary": "List Purchase Requests",
|
||||
"operationId": "list_purchase_requests_purchase_requests_get",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "status",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Status"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "limit",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"maximum": 200,
|
||||
"minimum": 1,
|
||||
"default": 50,
|
||||
"title": "Limit"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "offset",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"default": 0,
|
||||
"title": "Offset"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "X-CSRF-Token",
|
||||
"in": "header",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "X-Csrf-Token"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/PurchaseRequestListResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/purchase-requests/{request_id}": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"purchase-requests"
|
||||
],
|
||||
"summary": "Get Purchase Request",
|
||||
"operationId": "get_purchase_request_purchase_requests__request_id__get",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "request_id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"title": "Request Id"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "X-CSRF-Token",
|
||||
"in": "header",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "X-Csrf-Token"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/PurchaseRequestResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/ping": {
|
||||
"post": {
|
||||
"summary": "Ping",
|
||||
"operationId": "ping_ping_post",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "object",
|
||||
"title": "Response Ping Ping Post"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"CreatePurchaseRequestBody": {
|
||||
"properties": {
|
||||
"usdt_amount": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "number",
|
||||
"exclusiveMinimum": 0.0
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
|
||||
}
|
||||
],
|
||||
"title": "Usdt Amount"
|
||||
},
|
||||
"comment": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Comment"
|
||||
},
|
||||
"target_wallet_chain": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"maxLength": 16
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Target Wallet Chain",
|
||||
"default": "ETH"
|
||||
},
|
||||
"target_wallet_address": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"maxLength": 128
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Target Wallet Address"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"usdt_amount"
|
||||
],
|
||||
"title": "CreatePurchaseRequestBody"
|
||||
},
|
||||
"HTTPValidationError": {
|
||||
"properties": {
|
||||
"detail": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/ValidationError"
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Detail"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"title": "HTTPValidationError"
|
||||
},
|
||||
"MnemonicResponse": {
|
||||
"properties": {
|
||||
"mnemonic": {
|
||||
"type": "string",
|
||||
"title": "Mnemonic"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"mnemonic"
|
||||
],
|
||||
"title": "MnemonicResponse"
|
||||
},
|
||||
"OrganizationResponse": {
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"title": "Id"
|
||||
},
|
||||
"user_id": {
|
||||
"type": "string",
|
||||
"title": "User Id"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"title": "Name"
|
||||
},
|
||||
"short_name": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Short Name"
|
||||
},
|
||||
"inn": {
|
||||
"type": "string",
|
||||
"title": "Inn"
|
||||
},
|
||||
"ogrn": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Ogrn"
|
||||
},
|
||||
"kpp": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Kpp"
|
||||
},
|
||||
"legal_address": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Legal Address"
|
||||
},
|
||||
"actual_address": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Actual Address"
|
||||
},
|
||||
"bank_details": {
|
||||
"anyOf": [
|
||||
{
|
||||
"additionalProperties": true,
|
||||
"type": "object"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Bank Details"
|
||||
},
|
||||
"contact_person": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Contact Person"
|
||||
},
|
||||
"contact_phone": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Contact Phone"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"title": "Status"
|
||||
},
|
||||
"kyc_verified": {
|
||||
"type": "boolean",
|
||||
"title": "Kyc Verified"
|
||||
},
|
||||
"kyc_verified_at": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Kyc Verified At"
|
||||
},
|
||||
"has_wallets": {
|
||||
"type": "boolean",
|
||||
"title": "Has Wallets"
|
||||
},
|
||||
"created_by": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Created By"
|
||||
},
|
||||
"created_at": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Created At"
|
||||
},
|
||||
"updated_at": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Updated At"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"user_id",
|
||||
"name",
|
||||
"short_name",
|
||||
"inn",
|
||||
"ogrn",
|
||||
"kpp",
|
||||
"legal_address",
|
||||
"actual_address",
|
||||
"bank_details",
|
||||
"contact_person",
|
||||
"contact_phone",
|
||||
"status",
|
||||
"kyc_verified",
|
||||
"kyc_verified_at",
|
||||
"has_wallets",
|
||||
"created_by",
|
||||
"created_at",
|
||||
"updated_at"
|
||||
],
|
||||
"title": "OrganizationResponse"
|
||||
},
|
||||
"PurchaseRequestListResponse": {
|
||||
"properties": {
|
||||
"items": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/PurchaseRequestResponse"
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Items"
|
||||
},
|
||||
"total": {
|
||||
"type": "integer",
|
||||
"title": "Total"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"items",
|
||||
"total"
|
||||
],
|
||||
"title": "PurchaseRequestListResponse"
|
||||
},
|
||||
"PurchaseRequestResponse": {
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"title": "Id"
|
||||
},
|
||||
"organization_id": {
|
||||
"type": "string",
|
||||
"title": "Organization Id"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"title": "Status"
|
||||
},
|
||||
"usdt_amount": {
|
||||
"type": "string",
|
||||
"title": "Usdt Amount"
|
||||
},
|
||||
"rub_amount": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Rub Amount"
|
||||
},
|
||||
"exchange_rate": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Exchange Rate"
|
||||
},
|
||||
"service_fee_percent": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Service Fee Percent"
|
||||
},
|
||||
"comment": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Comment"
|
||||
},
|
||||
"admin_comment": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Admin Comment"
|
||||
},
|
||||
"target_wallet_chain": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Target Wallet Chain"
|
||||
},
|
||||
"target_wallet_address": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Target Wallet Address"
|
||||
},
|
||||
"tx_hash": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Tx Hash"
|
||||
},
|
||||
"created_at": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Created At"
|
||||
},
|
||||
"updated_at": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Updated At"
|
||||
},
|
||||
"completed_at": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Completed At"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"organization_id",
|
||||
"status",
|
||||
"usdt_amount",
|
||||
"rub_amount",
|
||||
"exchange_rate",
|
||||
"service_fee_percent",
|
||||
"comment",
|
||||
"admin_comment",
|
||||
"target_wallet_chain",
|
||||
"target_wallet_address",
|
||||
"tx_hash",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"completed_at"
|
||||
],
|
||||
"title": "PurchaseRequestResponse"
|
||||
},
|
||||
"SecretKeyResponse": {
|
||||
"properties": {
|
||||
"chain": {
|
||||
"type": "string",
|
||||
"title": "Chain"
|
||||
},
|
||||
"address": {
|
||||
"type": "string",
|
||||
"title": "Address"
|
||||
},
|
||||
"derivation_path": {
|
||||
"type": "string",
|
||||
"title": "Derivation Path"
|
||||
},
|
||||
"private_key": {
|
||||
"type": "string",
|
||||
"title": "Private Key"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"chain",
|
||||
"address",
|
||||
"derivation_path",
|
||||
"private_key"
|
||||
],
|
||||
"title": "SecretKeyResponse"
|
||||
},
|
||||
"ValidationError": {
|
||||
"properties": {
|
||||
"loc": {
|
||||
"items": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "integer"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "array",
|
||||
"title": "Location"
|
||||
},
|
||||
"msg": {
|
||||
"type": "string",
|
||||
"title": "Message"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"title": "Error Type"
|
||||
},
|
||||
"input": {
|
||||
"title": "Input"
|
||||
},
|
||||
"ctx": {
|
||||
"type": "object",
|
||||
"title": "Context"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"loc",
|
||||
"msg",
|
||||
"type"
|
||||
],
|
||||
"title": "ValidationError"
|
||||
},
|
||||
"WalletResponse": {
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"title": "Id"
|
||||
},
|
||||
"chain": {
|
||||
"type": "string",
|
||||
"title": "Chain"
|
||||
},
|
||||
"address": {
|
||||
"type": "string",
|
||||
"title": "Address"
|
||||
},
|
||||
"derivation_path": {
|
||||
"type": "string",
|
||||
"title": "Derivation Path"
|
||||
},
|
||||
"created_at": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"title": "Created At"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"chain",
|
||||
"address",
|
||||
"derivation_path",
|
||||
"created_at"
|
||||
],
|
||||
"title": "WalletResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
src/entities/commission/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export { TIERS, TIER_MIN, TIER_MAX, findTier, progressPercent } from './model/tiers'
|
||||
export type { Tier } from './model/tiers'
|
||||
export { CommissionTable } from './ui/CommissionTable'
|
||||
@@ -6,8 +6,8 @@ export interface Tier {
|
||||
|
||||
export const TIERS: readonly Tier[] = [
|
||||
{ min: 5_000, max: 30_000, pct: 8 },
|
||||
{ min: 30_000, max: 100_000, pct: 6 },
|
||||
{ min: 100_000, max: 600_000, pct: 4 },
|
||||
{ min: 30_001, max: 100_000, pct: 6 },
|
||||
{ min: 100_001, max: 600_000, pct: 4 },
|
||||
] as const
|
||||
|
||||
export const TIER_MIN = TIERS[0].min
|
||||
@@ -1,5 +1,5 @@
|
||||
import { TIERS } from '@widgets/currency-converter'
|
||||
import styles from './CommissionPanel.module.css'
|
||||
import { TIERS } from '../model/tiers'
|
||||
import styles from './CommissionTable.module.css'
|
||||
|
||||
const ru = (n: number) => n.toLocaleString('ru-RU')
|
||||
|
||||
@@ -10,7 +10,7 @@ interface Props {
|
||||
effectiveRate: number
|
||||
}
|
||||
|
||||
export function CommissionPanel({ amount, progress, commission, effectiveRate }: Props) {
|
||||
export function CommissionTable({ amount, progress, commission, effectiveRate }: Props) {
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.title}>КОМИССИЯ СЕРВИСА</div>
|
||||
@@ -1,37 +1,159 @@
|
||||
import { getCsrfToken } from '@shared/api/csrf'
|
||||
import { tokenStore } from '@shared/api/tokenStore'
|
||||
|
||||
const USERS_API_URL = 'https://app.users.elcsa.ru'
|
||||
|
||||
export type AccountType = 'individual' | 'legal_entity'
|
||||
|
||||
// Nested organization payload — present only on legal_entity accounts.
|
||||
export interface LegalEntityInfo {
|
||||
id: string
|
||||
name: string
|
||||
inn: string
|
||||
status: string
|
||||
short_name: string | null
|
||||
ogrn: string | null
|
||||
kpp: string | null
|
||||
legal_address: string | null
|
||||
actual_address: string | null
|
||||
bank_details: Record<string, unknown> | null
|
||||
contact_person: string | null
|
||||
contact_phone: string | null
|
||||
kyc_verified: boolean
|
||||
kyc_verified_at: string | null
|
||||
}
|
||||
|
||||
export interface MeResponse {
|
||||
id: string
|
||||
email: string
|
||||
first_name: string
|
||||
middle_name: string
|
||||
last_name: string
|
||||
birth_date: string
|
||||
crypto_wallet: string | null
|
||||
phone: string
|
||||
// Person fields are null on legal_entity accounts.
|
||||
first_name: string | null
|
||||
middle_name: string | null
|
||||
last_name: string | null
|
||||
birth_date: string | null
|
||||
encrypted_mnemonic: string | null
|
||||
phone: string | null
|
||||
passport_data: string | null
|
||||
inn: string | null
|
||||
erc20: string | null
|
||||
avatar_link: string | null
|
||||
kyc_verified: boolean
|
||||
is_deleted: boolean
|
||||
created_at: string
|
||||
updated_at: string
|
||||
kyc_verified_at: string | null
|
||||
webp_size_bytes?: number
|
||||
// "individual" -> физлицо, "legal_entity" -> аккаунт юр.лица.
|
||||
account_type: AccountType
|
||||
// Populated only for legal_entity accounts.
|
||||
legal_entity?: LegalEntityInfo | null
|
||||
}
|
||||
|
||||
export interface UploadAvatarPayload {
|
||||
photo_base64: string
|
||||
decoded_bytes: string
|
||||
}
|
||||
|
||||
async function authedHeaders(): Promise<HeadersInit> {
|
||||
const csrf = await getCsrfToken()
|
||||
const bearer = tokenStore.get()
|
||||
return {
|
||||
'X-CSRF-Token': csrf,
|
||||
...(bearer ? { Authorization: `Bearer ${bearer}` } : {}),
|
||||
}
|
||||
}
|
||||
|
||||
export async function getMe(): Promise<MeResponse> {
|
||||
const csrf = await getCsrfToken()
|
||||
const headers = await authedHeaders()
|
||||
|
||||
const res = await fetch(`${USERS_API_URL}/me/`, {
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'X-CSRF-Token': csrf,
|
||||
},
|
||||
headers,
|
||||
})
|
||||
|
||||
const data = await res.json()
|
||||
if (!res.ok) throw data
|
||||
return data
|
||||
}
|
||||
|
||||
export async function uploadAvatar(payload: UploadAvatarPayload): Promise<MeResponse> {
|
||||
const headers = await authedHeaders()
|
||||
|
||||
const res = await fetch(`${USERS_API_URL}/me/settings/avatar`, {
|
||||
method: 'PATCH',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...headers,
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
|
||||
const data = await res.json()
|
||||
if (!res.ok) throw data
|
||||
return data
|
||||
}
|
||||
|
||||
export interface PasswordResetStartPayload {
|
||||
email: string
|
||||
}
|
||||
|
||||
export async function passwordResetStart(payload: PasswordResetStartPayload): Promise<void> {
|
||||
const csrf = await getCsrfToken()
|
||||
const res = await fetch(`${USERS_API_URL}/me/settings/password/forgot/start`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-Token': csrf,
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
if (!res.ok) {
|
||||
const data = await res.json().catch(() => ({}))
|
||||
throw data
|
||||
}
|
||||
}
|
||||
|
||||
export async function updatePhone(phone: string): Promise<void> {
|
||||
const headers = await authedHeaders()
|
||||
|
||||
const res = await fetch(`${USERS_API_URL}/me/settings/phone`, {
|
||||
method: 'PATCH',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...headers,
|
||||
},
|
||||
body: JSON.stringify({ phone }),
|
||||
})
|
||||
|
||||
if (!res.ok) {
|
||||
const data = await res.json().catch(() => ({}))
|
||||
throw data
|
||||
}
|
||||
}
|
||||
|
||||
export interface PasswordResetCompletePayload {
|
||||
email: string
|
||||
code: string
|
||||
new_password: string
|
||||
confirm_password: string
|
||||
}
|
||||
|
||||
export async function passwordResetComplete(payload: PasswordResetCompletePayload): Promise<void> {
|
||||
const csrf = await getCsrfToken()
|
||||
const res = await fetch(`${USERS_API_URL}/me/settings/password/forgot/complete`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-Token': csrf,
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
if (!res.ok) {
|
||||
const data = await res.json().catch(() => ({}))
|
||||
throw data
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import type { UseQueryOptions } from '@tanstack/react-query'
|
||||
import { getMe } from '../api/profileApi'
|
||||
import type { MeResponse } from '../api/profileApi'
|
||||
|
||||
export function useMe() {
|
||||
type MeOptions = Pick<UseQueryOptions<MeResponse>, 'refetchInterval' | 'enabled'>
|
||||
|
||||
export function useMe(options?: MeOptions) {
|
||||
return useQuery<MeResponse>({
|
||||
queryKey: ['me'],
|
||||
queryFn: getMe,
|
||||
staleTime: Infinity,
|
||||
gcTime: Infinity,
|
||||
retry: false,
|
||||
...options,
|
||||
})
|
||||
}
|
||||
|
||||
12
src/features/auth/hooks/useUpdatePhone.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { updatePhone } from '../api/profileApi'
|
||||
|
||||
export function useUpdatePhone() {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation<void, unknown, string>({
|
||||
mutationFn: updatePhone,
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['me'] })
|
||||
},
|
||||
})
|
||||
}
|
||||
13
src/features/auth/hooks/useUploadAvatar.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { uploadAvatar } from '../api/profileApi'
|
||||
import type { MeResponse, UploadAvatarPayload } from '../api/profileApi'
|
||||
|
||||
export function useUploadAvatar() {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation<MeResponse, unknown, UploadAvatarPayload>({
|
||||
mutationFn: uploadAvatar,
|
||||
onSuccess: (data) => {
|
||||
queryClient.setQueryData(['me'], data)
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
export { registrationStart, registrationComplete, loginStart, loginComplete } from './api/registrationApi'
|
||||
export { getMe } from './api/profileApi'
|
||||
export type { MeResponse } from './api/profileApi'
|
||||
export { getMe, uploadAvatar, updatePhone } from './api/profileApi'
|
||||
export type { MeResponse, UploadAvatarPayload } from './api/profileApi'
|
||||
export { useMe } from './hooks/useMe'
|
||||
export { useUploadAvatar } from './hooks/useUploadAvatar'
|
||||
export { useUpdatePhone } from './hooks/useUpdatePhone'
|
||||
export type { RegistrationStartPayload, RegistrationCompletePayload, LoginStartPayload, LoginCompletePayload, AuthResponse } from './api/registrationApi'
|
||||
export { useIsAuthenticated } from './hooks/useIsAuthenticated'
|
||||
export { useAuth, AUTH_QUERY_KEY } from './hooks/useAuth'
|
||||
|
||||
126
src/features/b2b/api/b2bApi.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
import { getCsrfToken } from '@shared/api/csrf'
|
||||
import { refreshAccessToken } from '@shared/api/tokenStore'
|
||||
|
||||
const B2B_API_URL = 'https://app.b2b.elcsa.ru'
|
||||
|
||||
async function doB2bRequest<T>(
|
||||
path: string,
|
||||
options: RequestInit,
|
||||
allowRetry: boolean,
|
||||
): Promise<T> {
|
||||
const csrf = await getCsrfToken()
|
||||
|
||||
const res = await fetch(`${B2B_API_URL}${path}`, {
|
||||
...options,
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'X-CSRF-Token': csrf,
|
||||
...options.headers,
|
||||
},
|
||||
})
|
||||
|
||||
if (res.status === 401 && allowRetry) {
|
||||
try {
|
||||
await refreshAccessToken()
|
||||
return doB2bRequest<T>(path, options, false)
|
||||
} catch {
|
||||
throw new Error('Unauthorized')
|
||||
}
|
||||
}
|
||||
|
||||
const data = await res.json()
|
||||
if (!res.ok) throw data
|
||||
return data as T
|
||||
}
|
||||
|
||||
export interface CreatePurchaseRequestBody {
|
||||
usdt_amount: number | string
|
||||
comment?: string | null
|
||||
target_wallet_chain?: string | null
|
||||
target_wallet_address?: string | null
|
||||
}
|
||||
|
||||
export interface B2bPurchaseRequest {
|
||||
id: string
|
||||
organization_id: string
|
||||
status: string
|
||||
usdt_amount: string
|
||||
rub_amount: string | null
|
||||
exchange_rate: string | null
|
||||
service_fee_percent: string | null
|
||||
comment: string | null
|
||||
admin_comment: string | null
|
||||
target_wallet_chain: string | null
|
||||
target_wallet_address: string | null
|
||||
tx_hash: string | null
|
||||
created_at: string | null
|
||||
updated_at: string | null
|
||||
completed_at: string | null
|
||||
}
|
||||
|
||||
export interface B2bPurchaseRequestListResponse {
|
||||
items: B2bPurchaseRequest[]
|
||||
total: number
|
||||
}
|
||||
|
||||
export function createPurchaseRequest(
|
||||
body: CreatePurchaseRequestBody,
|
||||
): Promise<B2bPurchaseRequest> {
|
||||
return doB2bRequest('/purchase-requests', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body),
|
||||
}, true)
|
||||
}
|
||||
|
||||
export function getMyPurchaseRequests(
|
||||
limit = 50,
|
||||
offset = 0,
|
||||
): Promise<B2bPurchaseRequestListResponse> {
|
||||
return doB2bRequest(`/purchase-requests?limit=${limit}&offset=${offset}`, {}, true)
|
||||
}
|
||||
|
||||
// --- Organizations (legal-entity wallets) ---
|
||||
// Note: organizations endpoints live under /v1, unlike /purchase-requests.
|
||||
|
||||
export interface OrgWallet {
|
||||
id: string
|
||||
chain: string
|
||||
address: string
|
||||
derivation_path: string
|
||||
created_at: string | null
|
||||
}
|
||||
|
||||
export interface OrgAmountRaw {
|
||||
raw: string
|
||||
formatted: string
|
||||
decimals: number
|
||||
usd_price: string
|
||||
usd_value: string
|
||||
}
|
||||
|
||||
export interface OrgWalletBalance {
|
||||
id: string
|
||||
chain: string
|
||||
address: string
|
||||
derivation_path: string
|
||||
native_symbol: string
|
||||
native: OrgAmountRaw
|
||||
tokens: Record<string, OrgAmountRaw>
|
||||
total_usd: string
|
||||
error: string | null
|
||||
}
|
||||
|
||||
export interface OrgBalancesResponse {
|
||||
items: OrgWalletBalance[]
|
||||
total_usd: string
|
||||
has_errors: boolean
|
||||
}
|
||||
|
||||
export function getOrganizationWallets(): Promise<OrgWallet[]> {
|
||||
return doB2bRequest('/v1/organizations/wallets', {}, true)
|
||||
}
|
||||
|
||||
export function getOrganizationBalances(): Promise<OrgBalancesResponse> {
|
||||
return doB2bRequest('/v1/organizations/wallets/balances', {}, true)
|
||||
}
|
||||
13
src/features/b2b/hooks/useCreatePurchaseRequest.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import { createPurchaseRequest } from '../api/b2bApi'
|
||||
|
||||
export function useCreatePurchaseRequest() {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
return useMutation({
|
||||
mutationFn: createPurchaseRequest,
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['b2b', 'purchase-requests'] })
|
||||
},
|
||||
})
|
||||
}
|
||||
10
src/features/b2b/hooks/useMyPurchaseRequests.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { getMyPurchaseRequests } from '../api/b2bApi'
|
||||
|
||||
export function useMyPurchaseRequests() {
|
||||
return useQuery({
|
||||
queryKey: ['b2b', 'purchase-requests'],
|
||||
queryFn: () => getMyPurchaseRequests(),
|
||||
staleTime: 30_000,
|
||||
})
|
||||
}
|
||||
12
src/features/b2b/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export { useCreatePurchaseRequest } from './hooks/useCreatePurchaseRequest'
|
||||
export { useMyPurchaseRequests } from './hooks/useMyPurchaseRequests'
|
||||
export { getOrganizationWallets, getOrganizationBalances } from './api/b2bApi'
|
||||
export type {
|
||||
CreatePurchaseRequestBody,
|
||||
B2bPurchaseRequest,
|
||||
B2bPurchaseRequestListResponse,
|
||||
OrgWallet,
|
||||
OrgAmountRaw,
|
||||
OrgWalletBalance,
|
||||
OrgBalancesResponse,
|
||||
} from './api/b2bApi'
|
||||
@@ -62,6 +62,10 @@ export function getPaymentQuote(usdtAmount: number): Promise<PaymentQuote> {
|
||||
return doPaymentRequest(`/payment/quote?usdt_amount=${usdtAmount}`, {}, true)
|
||||
}
|
||||
|
||||
export function getPaymentQuoteByRub(rubAmount: number): Promise<PaymentQuote> {
|
||||
return doPaymentRequest(`/payment/quote/rub?total_rub=${rubAmount}`, {}, true)
|
||||
}
|
||||
|
||||
export interface CreateOrderPayload {
|
||||
usdt_amount: number
|
||||
usdt_exchange_rate: number
|
||||
@@ -103,3 +107,71 @@ export function createOrder(payload: CreateOrderPayload): Promise<OrderResult> {
|
||||
body: JSON.stringify(payload),
|
||||
}, true)
|
||||
}
|
||||
|
||||
export type OrderStatus = 'pending' | 'rejected' | 'completed' | 'cancelled' | 'error'
|
||||
|
||||
export type PaymentStatus =
|
||||
| 'pending'
|
||||
| 'money_accepted'
|
||||
| 'web3_processing'
|
||||
| 'web3_hash_error'
|
||||
| 'web3_balance_problem'
|
||||
| 'receipt_error'
|
||||
| 'completed'
|
||||
| 'usdt_delivered'
|
||||
|
||||
export interface Order {
|
||||
id: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
user_id: string
|
||||
usdt_amount: string
|
||||
usdt_exchange_rate: string
|
||||
gas_fee: string
|
||||
total_price: string
|
||||
service_fee: string
|
||||
status: OrderStatus
|
||||
client_payment_id: string
|
||||
itpay_payment_qr_url_desktop: string
|
||||
itpay_payment_qr_url_android: string
|
||||
itpay_payment_qr_url_ios: string
|
||||
itpay_payment_qr_image_desktop: string
|
||||
itpay_payment_qr_image_android: string
|
||||
itpay_payment_qr_image_ios: string
|
||||
itpay_id: string
|
||||
itpay_qr_id: string
|
||||
itpay_amount: string
|
||||
itpay_created_at: string
|
||||
}
|
||||
|
||||
export interface Payment {
|
||||
id: string
|
||||
created_at: string
|
||||
updated_at: string
|
||||
user_id: string
|
||||
order_id: string
|
||||
status: PaymentStatus
|
||||
receipt_cloudekassir_id: string
|
||||
receipt_cloudekassir_link: string
|
||||
itpay_payment_id: string
|
||||
itpay_paid_amount: string
|
||||
transaction_id: string
|
||||
web3_transaction_hash: string
|
||||
paid_at: string
|
||||
expired_date: string
|
||||
}
|
||||
|
||||
export interface OrderWithPayment {
|
||||
order: Order
|
||||
payment: Payment | null
|
||||
}
|
||||
|
||||
export interface OrdersResponse {
|
||||
orders: OrderWithPayment[]
|
||||
}
|
||||
|
||||
export const ORDERS_LIMIT = 20
|
||||
|
||||
export function getOrders(offset: number, limit: number = ORDERS_LIMIT): Promise<OrdersResponse> {
|
||||
return doPaymentRequest(`/payment/orders?offset=${offset}&limit=${limit}`, {}, true)
|
||||
}
|
||||
|
||||
15
src/features/payment/hooks/useOrders.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { useInfiniteQuery } from '@tanstack/react-query'
|
||||
import { getOrders, ORDERS_LIMIT } from '../api/paymentApi'
|
||||
|
||||
export function useOrders() {
|
||||
return useInfiniteQuery({
|
||||
queryKey: ['payment', 'orders'],
|
||||
queryFn: ({ pageParam }) => getOrders(pageParam as number),
|
||||
initialPageParam: 0,
|
||||
getNextPageParam: (lastPage, allPages) => {
|
||||
if (lastPage.orders.length < ORDERS_LIMIT) return undefined
|
||||
return allPages.length * ORDERS_LIMIT
|
||||
},
|
||||
staleTime: 30_000,
|
||||
})
|
||||
}
|
||||
13
src/features/payment/hooks/usePaymentQuoteByRub.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { getPaymentQuoteByRub } from '../api/paymentApi'
|
||||
import type { PaymentQuote } from '../api/paymentApi'
|
||||
|
||||
export function usePaymentQuoteByRub(rubAmount: number) {
|
||||
return useQuery<PaymentQuote>({
|
||||
queryKey: ['payment', 'quote', 'rub', rubAmount],
|
||||
queryFn: () => getPaymentQuoteByRub(rubAmount),
|
||||
enabled: rubAmount > 0,
|
||||
staleTime: 30_000,
|
||||
retry: false,
|
||||
})
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
export { usePaymentConfig } from './hooks/usePaymentConfig'
|
||||
export { usePaymentQuote } from './hooks/usePaymentQuote'
|
||||
export { usePaymentQuoteByRub } from './hooks/usePaymentQuoteByRub'
|
||||
export { useCreateOrder } from './hooks/useCreateOrder'
|
||||
export type { PaymentConfig, PaymentQuote, CreateOrderPayload, OrderResult } from './api/paymentApi'
|
||||
export { useOrders } from './hooks/useOrders'
|
||||
export { useCurrencyConversion } from './model/useCurrencyConversion'
|
||||
export type { PaymentConfig, PaymentQuote, CreateOrderPayload, OrderResult, Order, Payment, OrderWithPayment, OrderStatus, PaymentStatus } from './api/paymentApi'
|
||||
|
||||
102
src/features/payment/model/useCurrencyConversion.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import { useState } from 'react'
|
||||
import { useDebounce } from '@shared/lib/hooks/useDebounce'
|
||||
import { progressPercent } from '@entities/commission'
|
||||
import { GAS_PRICE, MIN_RUB_AMOUNT } from '@shared/config/constants'
|
||||
import type { ConvertFieldData } from '@shared/ui'
|
||||
import { usePaymentConfig } from '../hooks/usePaymentConfig'
|
||||
import { usePaymentQuote } from '../hooks/usePaymentQuote'
|
||||
import { usePaymentQuoteByRub } from '../hooks/usePaymentQuoteByRub'
|
||||
|
||||
const TOO_LARGE_ERROR = 'Сумма слишком большая и превышает 600 000 ₽'
|
||||
|
||||
const sanitize = (raw: string) => raw.replace(/[^0-9.]/g, '')
|
||||
|
||||
interface Options {
|
||||
/** Значение курса USDT/RUB до загрузки конфига (пилюля). */
|
||||
rateFallback?: number
|
||||
}
|
||||
|
||||
export function useCurrencyConversion({ rateFallback = 0 }: Options = {}) {
|
||||
const [direction, setDirection] = useState<'usdt_to_rub' | 'rub_to_usdt'>('usdt_to_rub')
|
||||
const [usdtInput, setUsdtInput] = useState('1000')
|
||||
const [rubInput, setRubInput] = useState(String(MIN_RUB_AMOUNT))
|
||||
|
||||
const { data: config } = usePaymentConfig()
|
||||
const configUsdtRate = Number(config?.usdt_exchange_rate) || rateFallback
|
||||
const gasPriceRub = Number(config?.gas_fee) || GAS_PRICE
|
||||
|
||||
const isUsdtToRub = direction === 'usdt_to_rub'
|
||||
|
||||
const numUsdt = Number.parseFloat(usdtInput) || 0
|
||||
const debouncedUsdt = useDebounce(numUsdt, 400)
|
||||
const { data: quoteUsdtToRub, isError: quoteError } = usePaymentQuote(isUsdtToRub ? debouncedUsdt : 0)
|
||||
|
||||
const numRubInput = Number.parseFloat(rubInput) || 0
|
||||
const debouncedRub = useDebounce(numRubInput, 400)
|
||||
const { data: quoteRubToUsdt, isError: quoteRubError } = usePaymentQuoteByRub(!isUsdtToRub ? debouncedRub : 0)
|
||||
|
||||
const rubBelowMin = !isUsdtToRub && numRubInput > 0 && numRubInput < MIN_RUB_AMOUNT
|
||||
|
||||
const rubTotal = quoteUsdtToRub?.total_price ?? ''
|
||||
const rubTotalNum = Number(rubTotal) || 0
|
||||
const usdtFromRub = quoteRubToUsdt?.usdt_amount ?? ''
|
||||
const usdtFromRubNum = Number(usdtFromRub) || 0
|
||||
|
||||
const commission = isUsdtToRub
|
||||
? Number(quoteUsdtToRub?.service_fee) || 0
|
||||
: Number(quoteRubToUsdt?.service_fee) || 0
|
||||
|
||||
const displayRubAmount = isUsdtToRub ? rubTotalNum : numRubInput
|
||||
const effectiveRate = isUsdtToRub
|
||||
? (numUsdt > 0 ? rubTotalNum / numUsdt : 0)
|
||||
: (usdtFromRubNum > 0 ? numRubInput / usdtFromRubNum : 0)
|
||||
|
||||
function onSwap() {
|
||||
setDirection(d => (d === 'usdt_to_rub' ? 'rub_to_usdt' : 'usdt_to_rub'))
|
||||
}
|
||||
|
||||
const convert: ConvertFieldData = isUsdtToRub
|
||||
? {
|
||||
value: usdtInput,
|
||||
currency: 'USDT',
|
||||
onChange: (raw) => setUsdtInput(sanitize(raw)),
|
||||
error: quoteError ? TOO_LARGE_ERROR : undefined,
|
||||
}
|
||||
: {
|
||||
value: rubInput,
|
||||
currency: 'RUB',
|
||||
onChange: (raw) => setRubInput(sanitize(raw)),
|
||||
error: rubBelowMin
|
||||
? `Минимальная сумма: ${MIN_RUB_AMOUNT.toLocaleString('ru-RU')} ₽`
|
||||
: quoteRubError
|
||||
? TOO_LARGE_ERROR
|
||||
: undefined,
|
||||
}
|
||||
|
||||
const pay: ConvertFieldData = isUsdtToRub
|
||||
? { value: rubTotal, currency: 'RUB' }
|
||||
: { value: usdtFromRub, currency: 'USDT' }
|
||||
|
||||
return {
|
||||
isUsdtToRub,
|
||||
gasPriceRub,
|
||||
configUsdtRate,
|
||||
convert,
|
||||
pay,
|
||||
onSwap,
|
||||
commission: {
|
||||
amount: displayRubAmount,
|
||||
progress: progressPercent(displayRubAmount),
|
||||
commission,
|
||||
effectiveRate,
|
||||
},
|
||||
// сырые значения для создания ордера и валидации в обёртках
|
||||
numUsdt,
|
||||
usdtFromRubNum,
|
||||
rubTotal,
|
||||
rubTotalNum,
|
||||
numRubInput,
|
||||
usdtFromRub,
|
||||
rubBelowMin,
|
||||
}
|
||||
}
|
||||
512
src/features/wallet/api/walletApi.ts
Normal file
@@ -0,0 +1,512 @@
|
||||
import { getCsrfToken } from '@shared/api/csrf'
|
||||
import { tokenStore, refreshAccessToken } from '@shared/api/tokenStore'
|
||||
import {
|
||||
getOrganizationWallets,
|
||||
getOrganizationBalances,
|
||||
type OrgAmountRaw,
|
||||
type OrgWalletBalance,
|
||||
} from '@features/b2b'
|
||||
|
||||
const WALLET_API_URL = 'https://app.cryptowallet.elcsa.ru'
|
||||
|
||||
export type Chain = 'ETH' | 'BSC' | 'BTC' | 'TRX' | 'SOL'
|
||||
|
||||
export interface FormattedAmount {
|
||||
raw: string
|
||||
formatted: string
|
||||
decimals: number
|
||||
usdPrice: number
|
||||
usdValue: number
|
||||
}
|
||||
|
||||
export interface WalletBalanceData {
|
||||
chain: Chain
|
||||
address: string
|
||||
native: FormattedAmount
|
||||
tokens: Record<string, FormattedAmount>
|
||||
}
|
||||
|
||||
export interface PriceEntry {
|
||||
usd: number
|
||||
}
|
||||
|
||||
export interface SendWalletPayload {
|
||||
to: string
|
||||
amount: string
|
||||
token?: string
|
||||
feeTier?: 'slow' | 'normal' | 'fast'
|
||||
}
|
||||
|
||||
export interface SendWalletResponse {
|
||||
data: { txid: string; chain: Chain }
|
||||
}
|
||||
|
||||
export interface WalletAddress {
|
||||
chain: Chain
|
||||
address: string
|
||||
derivationPath: string
|
||||
}
|
||||
|
||||
export interface PortfolioChain {
|
||||
chain: Chain
|
||||
address: string
|
||||
native: FormattedAmount
|
||||
tokens: Record<string, FormattedAmount>
|
||||
totalUsd: number
|
||||
stale: boolean
|
||||
lastUpdated: number
|
||||
}
|
||||
|
||||
export interface PortfolioData {
|
||||
totalUsd: number
|
||||
hasErrors: boolean
|
||||
perChain: Record<Chain, PortfolioChain>
|
||||
}
|
||||
|
||||
export const CHAINS: Chain[] = ['ETH', 'BSC', 'BTC', 'TRX', 'SOL']
|
||||
|
||||
async function walletGet<T>(path: string, allowRetry: boolean = true): Promise<T> {
|
||||
const csrf = await getCsrfToken()
|
||||
const bearer = tokenStore.get()
|
||||
|
||||
const res = await fetch(`${WALLET_API_URL}${path}`, {
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'X-CSRF-Token': csrf,
|
||||
...(bearer ? { Authorization: `Bearer ${bearer}` } : {}),
|
||||
},
|
||||
})
|
||||
|
||||
if (res.status === 401 && allowRetry) {
|
||||
try {
|
||||
await refreshAccessToken()
|
||||
return walletGet<T>(path, false)
|
||||
} catch {
|
||||
tokenStore.clear()
|
||||
throw new Error('Unauthorized')
|
||||
}
|
||||
}
|
||||
|
||||
const data = await res.json()
|
||||
if (!res.ok) throw data
|
||||
return data as T
|
||||
}
|
||||
|
||||
async function walletPost<T>(
|
||||
path: string,
|
||||
body: unknown,
|
||||
allowRetry: boolean = true,
|
||||
extraHeaders: Record<string, string> = {}
|
||||
): Promise<T> {
|
||||
const csrf = await getCsrfToken()
|
||||
const bearer = tokenStore.get()
|
||||
|
||||
const res = await fetch(`${WALLET_API_URL}${path}`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-Token': csrf,
|
||||
...(bearer ? { Authorization: `Bearer ${bearer}` } : {}),
|
||||
...extraHeaders,
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
})
|
||||
|
||||
if (res.status === 401 && allowRetry) {
|
||||
try {
|
||||
await refreshAccessToken()
|
||||
return walletPost<T>(path, body, false, extraHeaders)
|
||||
} catch {
|
||||
tokenStore.clear()
|
||||
throw new Error('Unauthorized')
|
||||
}
|
||||
}
|
||||
|
||||
const data = await res.json()
|
||||
if (!res.ok) throw data
|
||||
return data as T
|
||||
}
|
||||
|
||||
export async function getWalletAddresses(): Promise<WalletAddress[]> {
|
||||
const res = await walletGet<{ success: boolean; data: WalletAddress[] }>('/api/wallets')
|
||||
return res.data
|
||||
}
|
||||
|
||||
export async function getWalletBalance(chain: Chain): Promise<WalletBalanceData> {
|
||||
const res = await walletGet<{ success: boolean; data: WalletBalanceData }>(`/api/wallets/${chain}/balance`)
|
||||
return res.data
|
||||
}
|
||||
|
||||
export async function getPrices(symbols: string[]): Promise<Record<string, PriceEntry>> {
|
||||
const res = await walletGet<{ success: boolean; data: Record<string, PriceEntry> }>(
|
||||
`/api/prices?symbols=${symbols.join(',')}`
|
||||
)
|
||||
return res.data
|
||||
}
|
||||
|
||||
export async function sendWallet(chain: Chain, payload: SendWalletPayload): Promise<SendWalletResponse> {
|
||||
return walletPost<SendWalletResponse>(`/api/wallets/${chain}/send`, payload)
|
||||
}
|
||||
|
||||
export async function getPortfolio(): Promise<PortfolioData> {
|
||||
const res = await walletGet<{ success: boolean; data: PortfolioData }>('/api/wallets/portfolio')
|
||||
return res.data
|
||||
}
|
||||
|
||||
// --- Legal-entity (b2b organizations) adapters ---
|
||||
// The b2b balances endpoint returns the same information as the cryptowallet
|
||||
// portfolio, but with snake_case keys, string-encoded numbers and an items[]
|
||||
// array instead of a perChain map. These adapters normalise it into the
|
||||
// existing PortfolioData / WalletAddress shapes so the wallet UI is reused as-is.
|
||||
|
||||
const NATIVE_SYMBOL_TO_CHAIN: Record<string, Chain> = {
|
||||
ETH: 'ETH',
|
||||
BNB: 'BSC',
|
||||
BTC: 'BTC',
|
||||
TRX: 'TRX',
|
||||
SOL: 'SOL',
|
||||
}
|
||||
|
||||
/** Normalise a b2b chain/native_symbol into our Chain enum, or null if unknown. */
|
||||
function normalizeChain(chain: string, nativeSymbol?: string): Chain | null {
|
||||
const upper = chain?.toUpperCase()
|
||||
if ((CHAINS as string[]).includes(upper)) return upper as Chain
|
||||
const bySymbol = nativeSymbol ? NATIVE_SYMBOL_TO_CHAIN[nativeSymbol.toUpperCase()] : undefined
|
||||
return bySymbol ?? null
|
||||
}
|
||||
|
||||
function parseNum(value: string | number | null | undefined): number {
|
||||
const n = typeof value === 'number' ? value : parseFloat(value ?? '')
|
||||
return Number.isFinite(n) ? n : 0
|
||||
}
|
||||
|
||||
function toFormattedAmount(a: OrgAmountRaw): FormattedAmount {
|
||||
return {
|
||||
raw: a.raw,
|
||||
formatted: a.formatted,
|
||||
decimals: a.decimals,
|
||||
usdPrice: parseNum(a.usd_price),
|
||||
usdValue: parseNum(a.usd_value),
|
||||
}
|
||||
}
|
||||
|
||||
function toPortfolioChain(item: OrgWalletBalance, chain: Chain): PortfolioChain {
|
||||
const tokens: Record<string, FormattedAmount> = {}
|
||||
for (const [symbol, amount] of Object.entries(item.tokens ?? {})) {
|
||||
tokens[symbol] = toFormattedAmount(amount)
|
||||
}
|
||||
return {
|
||||
chain,
|
||||
address: item.address,
|
||||
native: toFormattedAmount(item.native),
|
||||
tokens,
|
||||
totalUsd: parseNum(item.total_usd),
|
||||
stale: false,
|
||||
lastUpdated: 0,
|
||||
}
|
||||
}
|
||||
|
||||
export async function getOrgPortfolio(): Promise<PortfolioData> {
|
||||
const res = await getOrganizationBalances()
|
||||
const perChain = {} as Record<Chain, PortfolioChain>
|
||||
for (const item of res.items ?? []) {
|
||||
const chain = normalizeChain(item.chain, item.native_symbol)
|
||||
if (!chain) continue
|
||||
perChain[chain] = toPortfolioChain(item, chain)
|
||||
}
|
||||
return {
|
||||
totalUsd: parseNum(res.total_usd),
|
||||
hasErrors: !!res.has_errors,
|
||||
perChain,
|
||||
}
|
||||
}
|
||||
|
||||
const EMPTY_AMOUNT: FormattedAmount = { raw: '0', formatted: '0', decimals: 0, usdPrice: 0, usdValue: 0 }
|
||||
|
||||
export async function getOrgWalletBalance(chain: Chain): Promise<WalletBalanceData> {
|
||||
const portfolio = await getOrgPortfolio()
|
||||
const c = portfolio.perChain[chain]
|
||||
if (!c) return { chain, address: '', native: EMPTY_AMOUNT, tokens: {} }
|
||||
return { chain, address: c.address, native: c.native, tokens: c.tokens }
|
||||
}
|
||||
|
||||
export async function getOrgWalletAddresses(): Promise<WalletAddress[]> {
|
||||
const wallets = await getOrganizationWallets()
|
||||
const result: WalletAddress[] = []
|
||||
for (const w of wallets ?? []) {
|
||||
const chain = normalizeChain(w.chain)
|
||||
if (!chain) continue
|
||||
result.push({ chain, address: w.address, derivationPath: w.derivation_path })
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export interface TokenInfo {
|
||||
chain: string
|
||||
symbol: string
|
||||
name: string
|
||||
contract: string | null
|
||||
}
|
||||
|
||||
export interface RelayQuotePayload {
|
||||
user: string
|
||||
recipient: string
|
||||
originChainId: number
|
||||
destinationChainId: number
|
||||
originCurrency: string
|
||||
destinationCurrency: string
|
||||
amount: string
|
||||
tradeType: 'EXACT_INPUT'
|
||||
}
|
||||
|
||||
export interface RelayQuoteResponse {
|
||||
details: {
|
||||
currencyOut: {
|
||||
amountFormatted: string
|
||||
amountUsd: string
|
||||
}
|
||||
}
|
||||
fees: {
|
||||
gas: {
|
||||
amountUsd: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function getTokensList(): Promise<TokenInfo[]> {
|
||||
const res = await walletGet<{ success: boolean; data: TokenInfo[] }>('/api/tokens')
|
||||
return res.data
|
||||
}
|
||||
|
||||
export interface JumperToken {
|
||||
address: string
|
||||
chainId: number
|
||||
symbol: string
|
||||
decimals: number
|
||||
name: string
|
||||
coinKey?: string
|
||||
logoURI?: string
|
||||
priceUSD: string
|
||||
}
|
||||
|
||||
export type JumperTokensMap = Record<string, JumperToken[]>
|
||||
|
||||
export async function getJumperTokens(): Promise<JumperTokensMap> {
|
||||
const res = await walletGet<{ tokens?: JumperTokensMap; data?: { tokens: JumperTokensMap } }>(
|
||||
'/api/jumper/tokens?chains=1,56,1151111081099710,728126428,20000000000001'
|
||||
)
|
||||
return res.data?.tokens ?? res.tokens ?? {}
|
||||
}
|
||||
|
||||
export interface JumperQuotePayload {
|
||||
fromChain: string
|
||||
toChain: string
|
||||
fromToken: string
|
||||
toToken: string
|
||||
fromAmount: string
|
||||
fromAddress: string
|
||||
toAddress: string
|
||||
slippage: number
|
||||
}
|
||||
|
||||
export interface JumperQuoteToken {
|
||||
address: string
|
||||
chainId: number
|
||||
symbol: string
|
||||
decimals: number
|
||||
name: string
|
||||
logoURI?: string
|
||||
priceUSD: string
|
||||
}
|
||||
|
||||
export interface JumperFeeCost {
|
||||
name: string
|
||||
description?: string
|
||||
token: JumperQuoteToken
|
||||
amount: string
|
||||
amountUSD: string
|
||||
percentage?: string
|
||||
included?: boolean
|
||||
}
|
||||
|
||||
export interface JumperQuote {
|
||||
type: string
|
||||
id: string
|
||||
tool: string
|
||||
toolDetails: { key: string; name: string; logoURI?: string }
|
||||
action: {
|
||||
fromToken: JumperQuoteToken
|
||||
fromAmount: string
|
||||
toToken: JumperQuoteToken
|
||||
fromChainId: number
|
||||
toChainId: number
|
||||
slippage: number
|
||||
fromAddress: string
|
||||
toAddress: string
|
||||
}
|
||||
estimate: {
|
||||
tool: string
|
||||
approvalAddress?: string
|
||||
toAmountMin: string
|
||||
toAmount: string
|
||||
fromAmount: string
|
||||
feeCosts?: JumperFeeCost[]
|
||||
}
|
||||
}
|
||||
|
||||
export async function getJumperQuote(payload: JumperQuotePayload): Promise<JumperQuote> {
|
||||
const qs = new URLSearchParams({
|
||||
fromChain: payload.fromChain,
|
||||
toChain: payload.toChain,
|
||||
fromToken: payload.fromToken,
|
||||
toToken: payload.toToken,
|
||||
fromAmount: payload.fromAmount,
|
||||
fromAddress: payload.fromAddress,
|
||||
toAddress: payload.toAddress,
|
||||
slippage: String(payload.slippage),
|
||||
}).toString()
|
||||
const res = await walletGet<JumperQuote & { body?: JumperQuote; data?: { body?: JumperQuote } }>(
|
||||
`/api/jumper/quote-best?${qs}`
|
||||
)
|
||||
return (res.data?.body ?? res.body ?? res) as JumperQuote
|
||||
}
|
||||
|
||||
export interface BridgeExecutePayload {
|
||||
provider: string
|
||||
fromChain: number
|
||||
toChain: number
|
||||
fromToken: string
|
||||
toToken: string
|
||||
fromAmount: string
|
||||
fromAddress: string
|
||||
toAddress: string
|
||||
acceptedMinOut?: string
|
||||
}
|
||||
|
||||
export interface BridgeExecuteResult {
|
||||
provider: string
|
||||
fromChain: number
|
||||
toChain: number
|
||||
toolName: string
|
||||
feeTxid?: string
|
||||
feeAmount?: string
|
||||
bridgeTxid: string
|
||||
fromAmount: string
|
||||
toAmountMin: string
|
||||
fromAmountUSD?: string
|
||||
toAmountUSD?: string
|
||||
trackerUrl?: string
|
||||
}
|
||||
|
||||
export async function executeBridge(payload: BridgeExecutePayload): Promise<BridgeExecuteResult> {
|
||||
const res = await walletPost<{ data?: { success: boolean; data: BridgeExecuteResult } }>(
|
||||
'/api/bridge/execute',
|
||||
payload,
|
||||
true,
|
||||
{ 'Idempotency-Key': crypto.randomUUID() }
|
||||
)
|
||||
return (res.data?.data ?? res) as BridgeExecuteResult
|
||||
}
|
||||
|
||||
export async function getRelayQuote(payload: RelayQuotePayload): Promise<RelayQuoteResponse> {
|
||||
return walletPost<RelayQuoteResponse>('/api/relay/quote', payload)
|
||||
}
|
||||
|
||||
export interface RelaySwapStep {
|
||||
id: string
|
||||
action: string
|
||||
description: string
|
||||
kind: string
|
||||
items: Array<{
|
||||
status: string
|
||||
data: {
|
||||
from: string
|
||||
to: string
|
||||
data: string
|
||||
value: string
|
||||
chainId: number
|
||||
gas: string
|
||||
maxFeePerGas: string
|
||||
maxPriorityFeePerGas: string
|
||||
}
|
||||
check: {
|
||||
endpoint: string
|
||||
method: string
|
||||
}
|
||||
}>
|
||||
requestId: string
|
||||
}
|
||||
|
||||
export interface RelaySwapResponse {
|
||||
steps: RelaySwapStep[]
|
||||
fees: RelayQuoteResponse['fees']
|
||||
details: {
|
||||
operation: string
|
||||
sender: string
|
||||
recipient: string
|
||||
currencyIn: { amount: string; amountFormatted: string; amountUsd: string; currency: { symbol: string } }
|
||||
currencyOut: { amount: string; amountFormatted: string; amountUsd: string; currency: { symbol: string } }
|
||||
totalImpact: { usd: string; percent: string }
|
||||
rate: string
|
||||
timeEstimate: number
|
||||
}
|
||||
}
|
||||
|
||||
export async function executeRelaySwap(payload: RelayQuotePayload): Promise<RelaySwapResponse> {
|
||||
return walletPost<RelaySwapResponse>('/api/relay/execute/swap', payload)
|
||||
}
|
||||
|
||||
export async function signRawEvmTx(
|
||||
chain: 'ETH' | 'BSC',
|
||||
txData: RelaySwapStep['items'][0]['data']
|
||||
): Promise<unknown> {
|
||||
const key = `relay-${chain.toLowerCase()}-${Date.now()}`
|
||||
return walletPost(`/api/wallets/${chain}/sign-raw-evm-tx`, txData, true, { 'Idempotency-Key': key })
|
||||
}
|
||||
|
||||
export async function signSolTx(txData: unknown): Promise<unknown> {
|
||||
return walletPost('/api/wallets/SOL/sign-and-broadcast-tx', txData)
|
||||
}
|
||||
|
||||
export interface TrxSwapQuotePayload {
|
||||
from: string
|
||||
to: string
|
||||
amountHuman: string
|
||||
}
|
||||
|
||||
export interface TrxSwapQuoteData {
|
||||
quoteId: string
|
||||
expiresIn: number
|
||||
expectedOutFormatted: string
|
||||
minOutFormatted: string
|
||||
fees: {
|
||||
network: { amountFormatted: string; asset: string; amountUsd: number }
|
||||
}
|
||||
}
|
||||
|
||||
export async function getTrxSwapQuote(payload: TrxSwapQuotePayload): Promise<TrxSwapQuoteData> {
|
||||
const res = await walletPost<{ success: boolean; data: TrxSwapQuoteData }>(
|
||||
'/api/wallets/TRX/swap/quote',
|
||||
payload
|
||||
)
|
||||
return res.data
|
||||
}
|
||||
|
||||
export async function executeTrxSwap(quoteId: string): Promise<unknown> {
|
||||
return walletPost(
|
||||
'/api/wallets/TRX/swap',
|
||||
{ quoteId },
|
||||
true,
|
||||
{ 'Idempotency-Key': `trx-${Date.now()}` }
|
||||
)
|
||||
}
|
||||
|
||||
export async function createWallet(): Promise<void> {
|
||||
await walletPost<unknown>('/api/wallets/create', {})
|
||||
}
|
||||
|
||||
export async function revealMnemonic(): Promise<string> {
|
||||
const res = await walletPost<{ success: boolean; data: { mnemonic: string } }>('/api/wallets/mnemonic/reveal', { confirm: 'I_UNDERSTAND_SEED_IS_SECRET' })
|
||||
return res.data.mnemonic
|
||||
}
|
||||
3
src/features/wallet/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export { useAllWalletBalances, usePrices, useSendWallet, useWalletAddresses, useWalletBalance, usePortfolio, useTokensList, useRelayQuote, useExecuteRelaySwap, useSignSwap, useTrxSwapQuote, useFetchTrxQuote, useExecuteTrxSwap, useJumperTokens, useJumperQuote, useFetchJumperQuote, useExecuteBridge, useCreateWallet, useRevealMnemonic } from './model/useWalletData'
|
||||
export type { Chain, FormattedAmount, WalletBalanceData, PriceEntry, SendWalletPayload, SendWalletResponse, WalletAddress, PortfolioData, PortfolioChain, TokenInfo, RelayQuotePayload, RelayQuoteResponse, RelaySwapResponse, RelaySwapStep, TrxSwapQuotePayload, TrxSwapQuoteData, JumperToken, JumperTokensMap, JumperQuote, JumperQuotePayload, JumperQuoteToken, JumperFeeCost, BridgeExecutePayload, BridgeExecuteResult } from './api/walletApi'
|
||||
export { CHAINS } from './api/walletApi'
|
||||
158
src/features/wallet/model/useWalletData.ts
Normal file
@@ -0,0 +1,158 @@
|
||||
import { useQuery, useQueries, useMutation } from '@tanstack/react-query'
|
||||
import { useMe } from '@features/auth'
|
||||
import { getWalletBalance, getPrices, sendWallet, getWalletAddresses, getPortfolio, getOrgPortfolio, getOrgWalletBalance, getOrgWalletAddresses, getTokensList, getRelayQuote, executeRelaySwap, signRawEvmTx, signSolTx, getTrxSwapQuote, executeTrxSwap, getJumperTokens, getJumperQuote, executeBridge, createWallet, revealMnemonic, CHAINS, type Chain, type SendWalletPayload, type RelayQuotePayload, type RelaySwapStep, type TrxSwapQuotePayload, type JumperQuotePayload, type BridgeExecutePayload } from '../api/walletApi'
|
||||
|
||||
/** Legal-entity accounts read wallet data from b2b instead of cryptowallet. */
|
||||
function useIsLegalAccount() {
|
||||
const { data: me } = useMe()
|
||||
return { isLegal: me?.account_type === 'legal_entity', ready: !!me }
|
||||
}
|
||||
|
||||
export function useWalletBalance(chain: Chain) {
|
||||
const { isLegal, ready } = useIsLegalAccount()
|
||||
return useQuery({
|
||||
queryKey: ['wallet', 'balance', chain, isLegal ? 'org' : 'self'],
|
||||
queryFn: isLegal ? () => getOrgWalletBalance(chain) : () => getWalletBalance(chain),
|
||||
enabled: isLegal ? ready : true,
|
||||
staleTime: 30_000,
|
||||
})
|
||||
}
|
||||
|
||||
export function useAllWalletBalances() {
|
||||
return useQueries({
|
||||
queries: CHAINS.map(chain => ({
|
||||
queryKey: ['wallet', 'balance', chain],
|
||||
queryFn: () => getWalletBalance(chain),
|
||||
staleTime: 30_000,
|
||||
})),
|
||||
})
|
||||
}
|
||||
|
||||
export function usePrices(symbols: string[]) {
|
||||
return useQuery({
|
||||
queryKey: ['wallet', 'prices', symbols.join(',')],
|
||||
queryFn: () => getPrices(symbols),
|
||||
staleTime: 5 * 60 * 1000,
|
||||
})
|
||||
}
|
||||
|
||||
export function useSendWallet() {
|
||||
return useMutation({
|
||||
mutationFn: ({ chain, ...payload }: { chain: Chain } & SendWalletPayload) =>
|
||||
sendWallet(chain, payload),
|
||||
})
|
||||
}
|
||||
|
||||
export function useWalletAddresses() {
|
||||
const { isLegal, ready } = useIsLegalAccount()
|
||||
return useQuery({
|
||||
queryKey: ['wallet', 'addresses', isLegal ? 'org' : 'self'],
|
||||
queryFn: isLegal ? getOrgWalletAddresses : getWalletAddresses,
|
||||
enabled: isLegal ? ready : true,
|
||||
staleTime: 10 * 60 * 1000,
|
||||
})
|
||||
}
|
||||
|
||||
export function usePortfolio() {
|
||||
const { isLegal, ready } = useIsLegalAccount()
|
||||
return useQuery({
|
||||
queryKey: ['wallet', 'portfolio', isLegal ? 'org' : 'self'],
|
||||
queryFn: isLegal ? getOrgPortfolio : getPortfolio,
|
||||
enabled: isLegal ? ready : true,
|
||||
staleTime: 30_000,
|
||||
})
|
||||
}
|
||||
|
||||
export function useTokensList() {
|
||||
return useQuery({
|
||||
queryKey: ['wallet', 'tokens'],
|
||||
queryFn: getTokensList,
|
||||
staleTime: 10 * 60 * 1000,
|
||||
})
|
||||
}
|
||||
|
||||
export function useJumperTokens() {
|
||||
return useQuery({
|
||||
queryKey: ['wallet', 'jumper', 'tokens'],
|
||||
queryFn: getJumperTokens,
|
||||
staleTime: 10 * 60 * 1000,
|
||||
})
|
||||
}
|
||||
|
||||
export function useJumperQuote(payload: JumperQuotePayload | null) {
|
||||
return useQuery({
|
||||
queryKey: ['wallet', 'jumper', 'quote',
|
||||
payload?.fromChain, payload?.toChain,
|
||||
payload?.fromToken, payload?.toToken,
|
||||
payload?.fromAmount, payload?.fromAddress, payload?.toAddress,
|
||||
],
|
||||
queryFn: () => getJumperQuote(payload!),
|
||||
enabled: !!payload,
|
||||
staleTime: 10_000,
|
||||
})
|
||||
}
|
||||
|
||||
export function useFetchJumperQuote() {
|
||||
return useMutation({ mutationFn: (payload: JumperQuotePayload) => getJumperQuote(payload) })
|
||||
}
|
||||
|
||||
export function useExecuteBridge() {
|
||||
return useMutation({ mutationFn: (payload: BridgeExecutePayload) => executeBridge(payload) })
|
||||
}
|
||||
|
||||
export function useCreateWallet() {
|
||||
return useMutation({ mutationFn: createWallet })
|
||||
}
|
||||
|
||||
export function useRevealMnemonic() {
|
||||
return useQuery({
|
||||
queryKey: ['wallet', 'mnemonic'],
|
||||
queryFn: revealMnemonic,
|
||||
staleTime: Infinity,
|
||||
retry: false,
|
||||
})
|
||||
}
|
||||
|
||||
export function useRelayQuote(payload: RelayQuotePayload | null) {
|
||||
return useQuery({
|
||||
queryKey: ['relay', 'quote',
|
||||
payload?.originChainId, payload?.destinationChainId,
|
||||
payload?.originCurrency, payload?.destinationCurrency, payload?.amount,
|
||||
],
|
||||
queryFn: () => getRelayQuote(payload!),
|
||||
enabled: !!payload,
|
||||
staleTime: 10_000,
|
||||
})
|
||||
}
|
||||
|
||||
export function useExecuteRelaySwap() {
|
||||
return useMutation({
|
||||
mutationFn: (payload: RelayQuotePayload) => executeRelaySwap(payload),
|
||||
})
|
||||
}
|
||||
|
||||
export function useSignSwap() {
|
||||
return useMutation({
|
||||
mutationFn: ({ chain, txData }: { chain: Chain; txData: unknown }) => {
|
||||
if (chain === 'SOL') return signSolTx(txData)
|
||||
return signRawEvmTx(chain as 'ETH' | 'BSC', txData as RelaySwapStep['items'][0]['data'])
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function useTrxSwapQuote(payload: TrxSwapQuotePayload | null) {
|
||||
return useQuery({
|
||||
queryKey: ['trx', 'quote', payload?.from, payload?.to, payload?.amountHuman],
|
||||
queryFn: () => getTrxSwapQuote(payload!),
|
||||
enabled: !!payload,
|
||||
staleTime: 10_000,
|
||||
})
|
||||
}
|
||||
|
||||
export function useFetchTrxQuote() {
|
||||
return useMutation({ mutationFn: getTrxSwapQuote })
|
||||
}
|
||||
|
||||
export function useExecuteTrxSwap() {
|
||||
return useMutation({ mutationFn: (quoteId: string) => executeTrxSwap(quoteId) })
|
||||
}
|
||||
5676
src/openapi.json
Normal file
1
src/pages/bridge/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { BridgePage } from './ui/BridgePage'
|
||||
12
src/pages/bridge/ui/BridgePage.module.css
Normal file
@@ -0,0 +1,12 @@
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 32px 20px 48px;
|
||||
}
|
||||
|
||||
@media (max-width: 650px) {
|
||||
.content {
|
||||
padding: 32px 20px;
|
||||
}
|
||||
}
|
||||
14
src/pages/bridge/ui/BridgePage.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { BridgeForm } from '@widgets/bridge-form'
|
||||
import { SwapBridgeTabs } from '@widgets/swap-bridge-tabs'
|
||||
import styles from './BridgePage.module.css'
|
||||
|
||||
export function BridgePage() {
|
||||
return (
|
||||
<>
|
||||
<SwapBridgeTabs active="bridge" />
|
||||
<div className={styles.content}>
|
||||
<BridgeForm />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
1
src/pages/converter-test/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { ConverterTestPage } from './ui/ConverterTestPage'
|
||||
133
src/pages/converter-test/ui/ConverterTestPage.module.css
Normal file
@@ -0,0 +1,133 @@
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 2rem 1rem;
|
||||
}
|
||||
|
||||
.wrap {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
max-width: 900px;
|
||||
background: var(--glass-bg);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 24px;
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: clamp(32px, 4vw, 48px);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 15px;
|
||||
line-height: 1.6;
|
||||
color: var(--text-secondary);
|
||||
margin-top: 12px;
|
||||
max-width: 560px;
|
||||
}
|
||||
|
||||
.body {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 48px;
|
||||
}
|
||||
|
||||
.formCol {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.hint {
|
||||
font-size: 12px;
|
||||
color: var(--highlight);
|
||||
margin-top: -12px;
|
||||
}
|
||||
|
||||
/* Панель условий / комиссии */
|
||||
.infoCol {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.infoTitle {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 1px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.infoRow {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 14px 18px;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border-radius: 10px;
|
||||
border: 1px solid var(--glass-border);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.infoRow[data-accent] {
|
||||
border-color: var(--grad-center);
|
||||
background: rgba(91, 61, 184, 0.12);
|
||||
}
|
||||
|
||||
.infoLabel {
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.infoValue {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.note {
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
color: var(--text-secondary);
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.submitBtn {
|
||||
width: 100%;
|
||||
margin-top: 40px;
|
||||
padding: 18px;
|
||||
border-radius: 12px;
|
||||
background: var(--grad-center);
|
||||
color: var(--text-primary);
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 1px;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.submitBtn:disabled {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.wrap {
|
||||
padding: 28px 20px;
|
||||
}
|
||||
|
||||
.body {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
}
|
||||
105
src/pages/converter-test/ui/ConverterTestPage.tsx
Normal file
@@ -0,0 +1,105 @@
|
||||
import { useState } from 'react'
|
||||
import { FormField } from '@shared/ui'
|
||||
import styles from './ConverterTestPage.module.css'
|
||||
|
||||
const MIN_ORDER = 500_000
|
||||
const APPROX_RATE = 0.03 // примерная комиссия 3% для крупных заявок
|
||||
|
||||
const ru = (n: number) => n.toLocaleString('ru-RU', { maximumFractionDigits: 0 })
|
||||
|
||||
export function ConverterTestPage() {
|
||||
const [amount, setAmount] = useState('')
|
||||
const [name, setName] = useState('')
|
||||
const [contact, setContact] = useState('')
|
||||
|
||||
const numAmount = Number(amount.replace(/\D/g, '')) || 0
|
||||
const belowMin = numAmount > 0 && numAmount < MIN_ORDER
|
||||
const commission = numAmount * APPROX_RATE
|
||||
|
||||
const handleAmountChange = (value: string) => {
|
||||
const digits = value.replace(/\D/g, '')
|
||||
setAmount(digits ? ru(Number(digits)) : '')
|
||||
}
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
// Тестовая страница — заявка никуда не отправляется.
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.page}>
|
||||
<form className={styles.wrap} onSubmit={handleSubmit}>
|
||||
<div className={styles.header}>
|
||||
<h1 className={styles.title}>Оставить заявку</h1>
|
||||
<p className={styles.subtitle}>
|
||||
Конвертация крупных объёмов по индивидуальному курсу. Оставьте заявку —
|
||||
менеджер свяжется с вами, подтвердит актуальный курс и сопроводит сделку.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={styles.body}>
|
||||
<div className={styles.formCol}>
|
||||
<FormField
|
||||
label="Объём заявки, ₽"
|
||||
type="text"
|
||||
value={amount}
|
||||
onChange={handleAmountChange}
|
||||
placeholder="от 500 000"
|
||||
/>
|
||||
{belowMin && (
|
||||
<p className={styles.hint}>
|
||||
Минимальный объём заявки — {ru(MIN_ORDER)} ₽
|
||||
</p>
|
||||
)}
|
||||
|
||||
<FormField
|
||||
label="Как к вам обращаться"
|
||||
type="text"
|
||||
value={name}
|
||||
onChange={setName}
|
||||
placeholder="Имя"
|
||||
/>
|
||||
|
||||
<FormField
|
||||
label="Email или телефон для связи"
|
||||
type="text"
|
||||
value={contact}
|
||||
onChange={setContact}
|
||||
placeholder="example@mail.ru / +7 900 000-00-00"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.infoCol}>
|
||||
<div className={styles.infoTitle}>УСЛОВИЯ</div>
|
||||
|
||||
<div className={styles.infoRow}>
|
||||
<span className={styles.infoLabel}>Минимальный объём</span>
|
||||
<span className={styles.infoValue}>{ru(MIN_ORDER)} ₽</span>
|
||||
</div>
|
||||
|
||||
<div className={styles.infoRow}>
|
||||
<span className={styles.infoLabel}>Примерная комиссия</span>
|
||||
<span className={styles.infoValue}>{(APPROX_RATE * 100).toFixed(0)} %</span>
|
||||
</div>
|
||||
|
||||
<div className={styles.infoRow} data-accent>
|
||||
<span className={styles.infoLabel}>Комиссия с объёма</span>
|
||||
<span className={styles.infoValue}>
|
||||
{numAmount > 0 ? `≈ ${ru(commission)} ₽` : '—'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p className={styles.note}>
|
||||
Итоговая комиссия рассчитывается индивидуально и зависит от объёма,
|
||||
валюты и направления сделки.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" className={styles.submitBtn} disabled={belowMin}>
|
||||
Оставить заявку
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,16 +1,15 @@
|
||||
import { useMe } from '@features/auth'
|
||||
import { Spinner } from '@shared/ui'
|
||||
import { ConverterSection } from '@widgets/converter-page'
|
||||
import { Footer } from '@widgets/footer'
|
||||
import { WalletHeader } from '@widgets/wallet-header'
|
||||
import styles from './ConverterPage.module.css'
|
||||
import { LegalConverterPage } from './LegalConverterPage'
|
||||
|
||||
export function ConverterPage() {
|
||||
return (
|
||||
<div className={styles.page}>
|
||||
<WalletHeader />
|
||||
<main className={styles.main}>
|
||||
<ConverterSection />
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
)
|
||||
const { data, isLoading } = useMe()
|
||||
|
||||
if (isLoading) {
|
||||
return <Spinner fullscreen size="lg" label="Загрузка данных аккаунта" />
|
||||
}
|
||||
|
||||
const isLegal = !!data && data.account_type !== 'individual'
|
||||
return isLegal ? <LegalConverterPage /> : <ConverterSection />
|
||||
}
|
||||
|
||||
125
src/pages/converter/ui/LegalConverterPage.module.css
Normal file
@@ -0,0 +1,125 @@
|
||||
.wrap {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
max-width: 900px;
|
||||
background: var(--glass-bg);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 24px;
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: clamp(32px, 4vw, 48px);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 15px;
|
||||
line-height: 1.6;
|
||||
color: var(--text-secondary);
|
||||
margin-top: 12px;
|
||||
max-width: 560px;
|
||||
}
|
||||
|
||||
.body {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 48px;
|
||||
}
|
||||
|
||||
.formCol {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.hint {
|
||||
font-size: 12px;
|
||||
color: var(--highlight);
|
||||
margin-top: -12px;
|
||||
}
|
||||
|
||||
/* Панель условий / комиссии */
|
||||
.infoCol {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.infoTitle {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 1px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.infoRow {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 14px 18px;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border-radius: 10px;
|
||||
border: 1px solid var(--glass-border);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.infoRow[data-accent] {
|
||||
border-color: var(--grad-center);
|
||||
background: rgba(91, 61, 184, 0.12);
|
||||
}
|
||||
|
||||
.infoLabel {
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.infoValue {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.note {
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
color: var(--text-secondary);
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.submitBtn {
|
||||
width: 100%;
|
||||
margin-top: 40px;
|
||||
padding: 18px;
|
||||
border-radius: 12px;
|
||||
background: var(--grad-center);
|
||||
color: var(--text-primary);
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 1px;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.submitBtn:disabled {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.wrap {
|
||||
padding: 28px 20px;
|
||||
}
|
||||
|
||||
.body {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
}
|
||||
221
src/pages/converter/ui/LegalConverterPage.tsx
Normal file
@@ -0,0 +1,221 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useCreatePurchaseRequest, useMyPurchaseRequests } from '@features/b2b'
|
||||
import type { B2bPurchaseRequest } from '@features/b2b'
|
||||
import { FormField, Notification, Select } from '@shared/ui'
|
||||
import styles from './LegalConverterPage.module.css'
|
||||
|
||||
const MIN_ORDER = 500_000
|
||||
|
||||
// Чем дольше пользователь готов ждать, тем ниже комиссия сервиса.
|
||||
const TERM_OPTIONS = [
|
||||
{ days: 3, rate: 0.05 },
|
||||
{ days: 4, rate: 0.04636 },
|
||||
{ days: 5, rate: 0.04273 },
|
||||
{ days: 6, rate: 0.03909 },
|
||||
{ days: 7, rate: 0.03545 },
|
||||
{ days: 8, rate: 0.03182 },
|
||||
{ days: 9, rate: 0.02818 },
|
||||
{ days: 10, rate: 0.02455 },
|
||||
{ days: 11, rate: 0.02091 },
|
||||
{ days: 12, rate: 0.01727 },
|
||||
{ days: 13, rate: 0.01364 },
|
||||
{ days: 14, rate: 0.01 },
|
||||
] as const
|
||||
|
||||
const ru = (n: number) => n.toLocaleString('ru-RU', { maximumFractionDigits: 0 })
|
||||
|
||||
// Имя и контакт у заявки отдельных полей не имеют — они упакованы в comment
|
||||
// (см. handleSubmit). Достаём их обратно для автозаполнения формы.
|
||||
const parseContactFromComment = (comment: string | null) => ({
|
||||
name: comment?.match(/Имя:\s*([^;]+)/)?.[1]?.trim() ?? '',
|
||||
contact: comment?.match(/Контакт:\s*([^;]+)/)?.[1]?.trim() ?? '',
|
||||
})
|
||||
|
||||
// Последняя по времени создания заявка на аккаунте, либо null.
|
||||
const latestRequest = (items: B2bPurchaseRequest[]): B2bPurchaseRequest | null =>
|
||||
items.reduce<B2bPurchaseRequest | null>(
|
||||
(latest, r) => (!latest || (r.created_at ?? '') > (latest.created_at ?? '') ? r : latest),
|
||||
null,
|
||||
)
|
||||
|
||||
const dayLabel = (days: number) => {
|
||||
const mod10 = days % 10
|
||||
const mod100 = days % 100
|
||||
if (mod10 === 1 && mod100 !== 11) return `${days} день`
|
||||
if (mod10 >= 2 && mod10 <= 4 && (mod100 < 10 || mod100 >= 20)) return `${days} дня`
|
||||
return `${days} дней`
|
||||
}
|
||||
|
||||
export function LegalConverterPage() {
|
||||
const [amount, setAmount] = useState('')
|
||||
const [name, setName] = useState('')
|
||||
const [contact, setContact] = useState('')
|
||||
const [days, setDays] = useState<number>(TERM_OPTIONS[0].days)
|
||||
const [notice, setNotice] = useState<'success' | 'error' | null>(null)
|
||||
|
||||
const { mutate: submitRequest, isPending } = useCreatePurchaseRequest()
|
||||
const { data: requests } = useMyPurchaseRequests()
|
||||
|
||||
// Автозаполнение имени и контакта из последней заявки аккаунта (один раз).
|
||||
const [prefilled, setPrefilled] = useState(false)
|
||||
useEffect(() => {
|
||||
if (prefilled || !requests) return
|
||||
const last = latestRequest(requests.items)
|
||||
if (!last) return
|
||||
const parsed = parseContactFromComment(last.comment)
|
||||
if (parsed.name) setName(parsed.name)
|
||||
if (parsed.contact) setContact(parsed.contact)
|
||||
setPrefilled(true)
|
||||
}, [prefilled, requests])
|
||||
|
||||
const numAmount = Number(amount.replace(/\D/g, '')) || 0
|
||||
const belowMin = numAmount > 0 && numAmount < MIN_ORDER
|
||||
|
||||
const rate = TERM_OPTIONS.find((o) => o.days === days)?.rate ?? TERM_OPTIONS[0].rate
|
||||
const commission = numAmount * rate
|
||||
const total = numAmount + commission
|
||||
|
||||
const handleAmountChange = (value: string) => {
|
||||
const digits = value.replace(/\D/g, '')
|
||||
setAmount(digits ? ru(Number(digits)) : '')
|
||||
}
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
if (numAmount === 0 || belowMin || isPending) return
|
||||
|
||||
// Отдельных полей под имя/контакт/срок/сумму в ₽ у API нет — всё уходит в comment.
|
||||
const comment = [
|
||||
name && `Имя: ${name}`,
|
||||
contact && `Контакт: ${contact}`,
|
||||
`Срок ожидания: ${dayLabel(days)}`,
|
||||
`Сумма: ${ru(numAmount)} ₽`,
|
||||
`Комиссия: ≈${ru(commission)} ₽`,
|
||||
`Итого: ≈${ru(total)} ₽`,
|
||||
].filter(Boolean).join('; ')
|
||||
|
||||
submitRequest(
|
||||
{ usdt_amount: numAmount, comment },
|
||||
{
|
||||
onSuccess: () => {
|
||||
setNotice('success')
|
||||
setAmount('')
|
||||
setName('')
|
||||
setContact('')
|
||||
setDays(TERM_OPTIONS[0].days)
|
||||
},
|
||||
onError: () => setNotice('error'),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<form className={styles.wrap} onSubmit={handleSubmit}>
|
||||
<div className={styles.header}>
|
||||
<h1 className={styles.title}>Оставить заявку</h1>
|
||||
<p className={styles.subtitle}>
|
||||
Конвертация крупных объёмов по индивидуальному курсу. Оставьте заявку —
|
||||
менеджер свяжется с вами, подтвердит актуальный курс и сопроводит сделку.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={styles.body}>
|
||||
<div className={styles.formCol}>
|
||||
<FormField
|
||||
label="Объём заявки, ₽"
|
||||
type="text"
|
||||
value={amount}
|
||||
onChange={handleAmountChange}
|
||||
placeholder="от 500 000"
|
||||
/>
|
||||
{belowMin && (
|
||||
<p className={styles.hint}>
|
||||
Минимальный объём заявки — {ru(MIN_ORDER)} ₽
|
||||
</p>
|
||||
)}
|
||||
|
||||
<Select
|
||||
id="term"
|
||||
label="Срок ожидания операции"
|
||||
value={days}
|
||||
onChange={setDays}
|
||||
options={TERM_OPTIONS.map((o) => ({
|
||||
value: o.days,
|
||||
label: `${dayLabel(o.days)} — комиссия ${(o.rate * 100).toFixed(3)} %`,
|
||||
}))}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
label="Как к вам обращаться"
|
||||
type="text"
|
||||
value={name}
|
||||
onChange={setName}
|
||||
placeholder="Имя"
|
||||
/>
|
||||
|
||||
<FormField
|
||||
label="Email или телефон для связи"
|
||||
type="text"
|
||||
value={contact}
|
||||
onChange={setContact}
|
||||
placeholder="example@mail.ru / +7 900 000-00-00"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.infoCol}>
|
||||
<div className={styles.infoTitle}>УСЛОВИЯ</div>
|
||||
|
||||
<div className={styles.infoRow}>
|
||||
<span className={styles.infoLabel}>Минимальный объём</span>
|
||||
<span className={styles.infoValue}>{ru(MIN_ORDER)} ₽</span>
|
||||
</div>
|
||||
|
||||
<div className={styles.infoRow}>
|
||||
<span className={styles.infoLabel}>Срок ожидания</span>
|
||||
<span className={styles.infoValue}>{dayLabel(days)}</span>
|
||||
</div>
|
||||
|
||||
<div className={styles.infoRow}>
|
||||
<span className={styles.infoLabel}>Ставка комиссии</span>
|
||||
<span className={styles.infoValue}>{(rate * 100).toFixed(3)} %</span>
|
||||
</div>
|
||||
|
||||
<div className={styles.infoRow}>
|
||||
<span className={styles.infoLabel}>Сумма комиссии</span>
|
||||
<span className={styles.infoValue}>
|
||||
{numAmount > 0 ? `≈ ${ru(commission)} ₽` : '—'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className={styles.infoRow} data-accent>
|
||||
<span className={styles.infoLabel}>Итого к оплате</span>
|
||||
<span className={styles.infoValue}>
|
||||
{numAmount > 0 ? `≈ ${ru(total)} ₽` : '—'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<p className={styles.note}>
|
||||
Итоговая комиссия рассчитывается индивидуально и зависит от объёма,
|
||||
валюты и направления сделки.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
className={styles.submitBtn}
|
||||
disabled={belowMin || numAmount === 0 || isPending}
|
||||
>
|
||||
{isPending ? 'Отправляем...' : 'Оставить заявку'}
|
||||
</button>
|
||||
|
||||
{notice && (
|
||||
<Notification
|
||||
status={notice}
|
||||
message={notice === 'success' ? 'Заявка отправлена' : 'Не удалось отправить заявку'}
|
||||
onClose={() => setNotice(null)}
|
||||
/>
|
||||
)}
|
||||
</form>
|
||||
)
|
||||
}
|
||||
@@ -1,7 +1,15 @@
|
||||
import { Navigate } from 'react-router-dom'
|
||||
import { useMe } from '@features/auth'
|
||||
import { ROUTES } from '@shared/config/routes'
|
||||
import { KycWidget } from '@widgets/kyc-verification'
|
||||
import styles from './KycPage.module.css'
|
||||
|
||||
export function KycPage() {
|
||||
const { data, isLoading } = useMe()
|
||||
|
||||
if (isLoading) return null
|
||||
if (data?.kyc_verified) return <Navigate to={ROUTES.PROFILE} replace />
|
||||
|
||||
return (
|
||||
<div className={styles.page}>
|
||||
<KycWidget />
|
||||
|
||||
1
src/pages/politika-cookie/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { PolitikaCookiePage } from './ui/PolitikaCookiePage'
|
||||
115
src/pages/politika-cookie/ui/PolitikaCookiePage.module.css
Normal file
@@ -0,0 +1,115 @@
|
||||
.main {
|
||||
padding: 40px 20px;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.container {
|
||||
background: var(--bg-mid, #1b1547);
|
||||
padding: 40px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
color: var(--text-primary, #ffffff);
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.sectionTitle {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 15px;
|
||||
color: var(--text-primary, #ffffff);
|
||||
border-bottom: 2px solid var(--interactive, #4a6dff);
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.subSectionTitle {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 10px;
|
||||
color: var(--text-primary, #ffffff);
|
||||
}
|
||||
|
||||
.list {
|
||||
list-style: disc;
|
||||
margin-left: 20px;
|
||||
line-height: 1.8;
|
||||
color: var(--text-primary, #ffffff);
|
||||
}
|
||||
|
||||
.list li {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.list strong {
|
||||
color: var(--text-primary, #ffffff);
|
||||
}
|
||||
|
||||
.info {
|
||||
padding: 20px;
|
||||
background: var(--glass-bg, rgba(255, 255, 255, 0.04));
|
||||
border-left: 4px solid var(--interactive, #4a6dff);
|
||||
border-radius: 4px;
|
||||
margin: 15px 0;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.info p {
|
||||
margin: 5px 0;
|
||||
color: var(--text-primary, #ffffff);
|
||||
}
|
||||
|
||||
.example {
|
||||
padding: 10px 15px;
|
||||
background: var(--glass-bg, rgba(255, 255, 255, 0.04));
|
||||
border-left: 3px solid var(--interactive, #4a6dff);
|
||||
border-radius: 4px;
|
||||
font-style: italic;
|
||||
color: var(--text-primary, #ffffff);
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.warning {
|
||||
padding: 15px;
|
||||
background: #fff3cd;
|
||||
border-left: 4px solid #ffc107;
|
||||
border-radius: 4px;
|
||||
color: var(--text-primary, #ffffff);
|
||||
margin: 15px 0;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.sectionTitle {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.subSectionTitle {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.list {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.info {
|
||||
padding: 15px;
|
||||
}
|
||||
}
|
||||
274
src/pages/politika-cookie/ui/PolitikaCookiePage.tsx
Normal file
@@ -0,0 +1,274 @@
|
||||
import { Footer } from '@widgets/footer'
|
||||
import { Header } from '@widgets/header'
|
||||
import styles from './PolitikaCookiePage.module.css'
|
||||
|
||||
export function PolitikaCookiePage() {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<main className={styles.main}>
|
||||
<div className={styles.container}>
|
||||
<h1 className={styles.title}>ПОЛИТИКА ИСПОЛЬЗОВАНИЯ ФАЙЛОВ COOKIE</h1>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>Общие положения и терминология</h3>
|
||||
<p>
|
||||
Настоящая Политика использования файлов cookie устанавливает порядок обработки файлов cookie и содержащихся в них персональных данных ООО «БИТФОРС» при использовании пользователями интернет-ресурса https://bitforce-foundation.ru.
|
||||
</p>
|
||||
<p>
|
||||
Файлы cookie — это текстовые файлы небольшого размера, которые устанавливаются на пользовательское устройство при посещении интернет-ресурса или совершении на нем определенных действий. Файлы cookie остаются сохраненными на устройстве даже после покидания ресурса, что позволяет «узнавать» пользователя при последующих посещениях.
|
||||
</p>
|
||||
<p>
|
||||
К персональным данным относится не сам файл cookie, а его содержимое — уникальные идентификаторы, IP-адреса, информация о предпочтениях пользователя и другие данные, позволяющие прямо или косвенно идентифицировать физическое лицо.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>Оператор персональных данных</h3>
|
||||
<p>Оператором персональных данных, содержащихся в файлах cookie, является:</p>
|
||||
<div className={styles.info}>
|
||||
<p>ООО «БИТФОРС»</p>
|
||||
<p>ИНН: 9810001062</p>
|
||||
<p>ОГРН: 1257800060990</p>
|
||||
<p>Юридический адрес: 196246, город Санкт-Петербург, Московское ш, д. 25 к. 1 литера В, помещ. 3-н</p>
|
||||
</div>
|
||||
<p>
|
||||
Оператор определяет цели обработки персональных данных, их состав, а также действия с персональными данными, включая случаи использования сторонних файлов cookie.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>Категории файлов cookie и их назначение</h3>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>1. Строго необходимые (технические) файлы cookie</h4>
|
||||
<p>
|
||||
Данные файлы обеспечивают работу интернет-ресурса и предоставление необходимого уровня сервиса: авторизацию, навигацию, отображение контента в соответствии с параметрами устройства, обеспечение безопасности.
|
||||
</p>
|
||||
<p>
|
||||
Обработка таких файлов cookie осуществляется на основании п. 5 ч. 1 ст. 6 ФЗ № 152 (заключение и исполнение договора). Согласие на использование строго необходимых файлов cookie не требуется.
|
||||
</p>
|
||||
<p className={styles.example}>Примеры: файлы сессий (PHPSESSID), настройки безопасности, файлы аутентификации.</p>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>2. Функциональные файлы cookie</h4>
|
||||
<p>
|
||||
Используются для запоминания пользовательских предпочтений и персонализации взаимодействия с сайтом: сохранение выбранного языка, региона, настроек отображения, размера шрифта.
|
||||
</p>
|
||||
<p>
|
||||
Обработка осуществляется на основании согласия субъекта персональных данных, поскольку данная обработка не является строго необходимой для функционирования сайта.
|
||||
</p>
|
||||
<p className={styles.example}>Примеры: настройки языка интерфейса, предпочтения отображения, настройки доступности.</p>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>3. Аналитические файлы cookie</h4>
|
||||
<p>
|
||||
Собирают информацию о взаимодействии пользователей с интернет-ресурсом для анализа его использования, выявления популярных разделов, обнаружения ошибок и улучшения пользовательского опыта. Могут содержать персональные данные, включая IP-адреса пользователей.
|
||||
</p>
|
||||
<p>
|
||||
Обработка осуществляется на основании согласия субъекта персональных данных.
|
||||
</p>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>4. Маркетинговые файлы cookie</h4>
|
||||
<p>
|
||||
Используются для отслеживания пользователей в целях персонализированной рекламы, анализа эффективности рекламных кампаний, ретаргетинга.
|
||||
</p>
|
||||
<p>
|
||||
Обработка осуществляется исключительно на основании согласия субъекта персональных данных.
|
||||
</p>
|
||||
<p className={styles.example}>Примеры: пиксели социальных сетей, рекламные идентификаторы, файлы ретаргетинга.</p>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>Правовые основания обработки персональных данных</h3>
|
||||
<p>Обработка персональных данных, содержащихся в файлах cookie, осуществляется на следующих правовых основаниях:</p>
|
||||
<ul className={styles.list}>
|
||||
<li>
|
||||
<strong>Согласие субъекта персональных данных</strong> — для функциональных, аналитических и маркетинговых файлов cookie
|
||||
</li>
|
||||
<li>
|
||||
<strong>Заключение и исполнение договора</strong> — для строго необходимых файлов cookie, обеспечивающих работу интернет-ресурса
|
||||
</li>
|
||||
<li>
|
||||
<strong>Законные интересы оператора</strong> — в исключительных случаях, когда отсутствуют иные основания
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>Порядок получения согласия</h3>
|
||||
<h4 className={styles.subSectionTitle}>Принципы получения согласия:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Согласие должно быть получено до начала обработки персональных данных</li>
|
||||
<li>Информация об использовании файлов cookie размещается на первом уровне интернет-ресурса</li>
|
||||
<li>Предоставляется возможность выбора категорий файлов cookie</li>
|
||||
<li>Используются активные формулировки вместо пассивных</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>Критерии действительного согласия:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>
|
||||
<strong>Добровольность</strong> — согласие дается по свободной воле субъекта
|
||||
</li>
|
||||
<li>
|
||||
<strong>Конкретность</strong> — четко определены цели обработки
|
||||
</li>
|
||||
<li>
|
||||
<strong>Информированность</strong> — предоставлена полная информация об обработке
|
||||
</li>
|
||||
<li>
|
||||
<strong>Однозначность</strong> — согласие выражено в недвусмысленной форме
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>Сторонние файлы cookie</h3>
|
||||
<h4 className={styles.subSectionTitle}>Использование сторонних сервисов:</h4>
|
||||
<p>Наш интернет-ресурс использует файлы cookie сторонних сервисов, включая:</p>
|
||||
<ul className={styles.list}>
|
||||
<li>Яндекс.Метрика (ООО «ЯНДЕКС», Россия)</li>
|
||||
<li>Социальные сети и сервисы интеграции</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>Обеспечение защиты:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Получено согласие на передачу</li>
|
||||
<li>Применяются дополнительные меры защиты данных</li>
|
||||
<li>Контролируется соблюдение принципов обработки персональных данных получателями</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>Сроки обработки и хранения</h3>
|
||||
<h4 className={styles.subSectionTitle}>Категории по срокам хранения:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Сеансовые cookie — удаляются автоматически при закрытии браузера</li>
|
||||
<li>Постоянные cookie — хранятся установленный период или до удаления пользователем</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>Конкретные сроки:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Необходимые файлы cookie — до 12 месяцев</li>
|
||||
<li>Функциональные файлы cookie — до 12 месяцев</li>
|
||||
<li>Аналитические файлы cookie — до 24 месяцев</li>
|
||||
<li>Маркетинговые файлы cookie — до 24 месяцев</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
По истечении установленных сроков файлы cookie удаляются автоматически. Пользователь может удалить файлы cookie досрочно через настройки браузера или отозвать согласие на их обработку.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>Права субъектов персональных данных</h3>
|
||||
<h4 className={styles.subSectionTitle}>Право на информацию:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Получение информации о обработке персональных данных</li>
|
||||
<li>Сведения о правовых основаниях и целях обработки</li>
|
||||
<li>Информация о сроках обработки и составе данных</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>Право на доступ:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Получение подтверждения факта обработки</li>
|
||||
<li>Ознакомление с обрабатываемыми персональными данными</li>
|
||||
<li>Получение информации об источниках персональных данных</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>Право на уточнение, блокирование, удаление:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Требование уточнения неточных данных</li>
|
||||
<li>Блокирование недостоверных данных</li>
|
||||
<li>Удаление незаконно полученных данных</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>Право на отзыв согласия:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Отзыв согласия в любое время</li>
|
||||
<li>Прекращение обработки после отзыва согласия</li>
|
||||
<li>Сохранение права на обжалование действий оператора</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>Способы управления файлами cookie</h3>
|
||||
<h4 className={styles.subSectionTitle}>Управление через настройки сайта:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Использование баннера согласия на файлы cookie</li>
|
||||
<li>Изменение настроек в любое время через интерфейс сайта</li>
|
||||
<li>Отзыв согласия на использование отдельных категорий файлов cookie</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>Управление через браузер:</h4>
|
||||
<p>Большинство браузеров позволяют контролировать файлы cookie:</p>
|
||||
<ul className={styles.list}>
|
||||
<li>Блокировка — запрет установки новых файлов cookie</li>
|
||||
<li>Удаление — очистка существующих файлов cookie</li>
|
||||
<li>Уведомления — получение предупреждений при установке файлов cookie</li>
|
||||
<li>Селективная настройка — разрешение файлов cookie только для определенных сайтов</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>Инструкции для популярных браузеров:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Google Chrome: Настройки → Конфиденциальность и безопасность → Файлы cookie</li>
|
||||
<li>Mozilla Firefox: Настройки → Приватность и Защита → Файлы cookie</li>
|
||||
<li>Safari: Настройки → Конфиденциальность → Файлы cookie</li>
|
||||
<li>Microsoft Edge: Настройки → Файлы cookie и разрешения сайтов</li>
|
||||
</ul>
|
||||
|
||||
<p className={styles.warning}>
|
||||
Блокировка необходимых файлов cookie может привести к ограничению функциональности интернет-ресурса.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>Меры безопасности</h3>
|
||||
<p>
|
||||
Оператор применяет правовые, организационные и технические меры для защиты персональных данных:
|
||||
</p>
|
||||
<h4 className={styles.subSectionTitle}>Правовые меры:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Назначение ответственного за организацию обработки персональных данных</li>
|
||||
<li>Ознакомление сотрудников с требованиями законодательства</li>
|
||||
<li>Заключение соглашений о неразглашении персональных данных</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>Организационные меры:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Определение перечня лиц, допущенных к обработке персональных данных</li>
|
||||
<li>Установление правил доступа к персональным данным</li>
|
||||
<li>Контроль за соблюдением требований по защите персональных данных</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>Технические меры:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Использование средств защиты информации</li>
|
||||
<li>Применение криптографических средств защиты</li>
|
||||
<li>Обеспечение целостности и доступности персональных данных</li>
|
||||
<li>Регулярное обновление систем защиты информации</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>Контактная информация и обращения</h3>
|
||||
<p>Для реализации прав субъекта персональных данных обращайтесь к нам:</p>
|
||||
<div className={styles.info}>
|
||||
<p>ООО «БИТФОРС»</p>
|
||||
<p>ИНН: 9810001062</p>
|
||||
<p>ОГРН: 1257800060990</p>
|
||||
<p>Юридический адрес: 196246, город Санкт-Петербург, Московское ш, д. 25 к. 1 литера В, помещ. 3-н</p>
|
||||
<p>Email компании: company@bitforcefoundation.ru</p>
|
||||
</div>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>Порядок рассмотрения обращений:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Срок рассмотрения обращений — до 30 дней с момента получения</li>
|
||||
<li>Обращения рассматриваются в письменной форме</li>
|
||||
<li>Ответ направляется способом, указанным в обращении</li>
|
||||
<li>При отказе в удовлетворении требований указываются мотивированные основания</li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
1
src/pages/politika-personalnyh-dannyh/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { PolitikaPage } from './ui/PolitikaPage'
|
||||
138
src/pages/politika-personalnyh-dannyh/ui/PolitikaPage.module.css
Normal file
@@ -0,0 +1,138 @@
|
||||
.main {
|
||||
padding: 40px 20px;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.container {
|
||||
background: var(--bg-mid, #1b1547);
|
||||
padding: 40px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
color: var(--text-primary, #ffffff);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
color: var(--text-secondary, #b5b0cc);
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.sectionTitle {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 15px;
|
||||
color: var(--text-primary, #ffffff);
|
||||
border-bottom: 2px solid var(--interactive, #4a6dff);
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.subSectionTitle {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 10px;
|
||||
color: var(--text-primary, #ffffff);
|
||||
}
|
||||
|
||||
.definitions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.definition {
|
||||
padding: 15px;
|
||||
background: var(--glass-bg, rgba(255, 255, 255, 0.04));
|
||||
border-left: 4px solid var(--interactive, #4a6dff);
|
||||
border-radius: 4px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.list {
|
||||
list-style: disc;
|
||||
margin-left: 20px;
|
||||
line-height: 1.8;
|
||||
color: var(--text-primary, #ffffff);
|
||||
}
|
||||
|
||||
.list li {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.goalsList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.goal {
|
||||
padding: 15px;
|
||||
background: var(--glass-bg, rgba(255, 255, 255, 0.04));
|
||||
border-left: 4px solid var(--interactive, #4a6dff);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.goal strong {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
color: var(--text-primary, #ffffff);
|
||||
}
|
||||
|
||||
.goal ul {
|
||||
list-style: disc;
|
||||
margin-left: 20px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.goal li {
|
||||
margin-bottom: 6px;
|
||||
color: var(--text-primary, #ffffff);
|
||||
}
|
||||
|
||||
.contacts {
|
||||
padding: 20px;
|
||||
background: var(--glass-bg, rgba(255, 255, 255, 0.04));
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--glass-border, rgba(255, 255, 255, 0.08));
|
||||
line-height: 1.8;
|
||||
color: var(--text-primary, #ffffff);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.sectionTitle {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.subSectionTitle {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.list {
|
||||
margin-left: 15px;
|
||||
}
|
||||
}
|
||||
300
src/pages/politika-personalnyh-dannyh/ui/PolitikaPage.tsx
Normal file
@@ -0,0 +1,300 @@
|
||||
import { Footer } from '@widgets/footer'
|
||||
import { Header } from '@widgets/header'
|
||||
import styles from './PolitikaPage.module.css'
|
||||
|
||||
export function PolitikaPage() {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<main className={styles.main}>
|
||||
<div className={styles.container}>
|
||||
<h1 className={styles.title}>ПОЛИТИКА ОБРАБОТКИ ПЕРСОНАЛЬНЫХ ДАННЫХ</h1>
|
||||
<h2 className={styles.subtitle}>ООО «БИТФОРС»</h2>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>1. Общие положения</h3>
|
||||
<p>
|
||||
Настоящая Политика обработки персональных данных разработана в соответствии с Федеральным законом от 27.07.2006 № 152-ФЗ «О персональных данных» и определяет порядок обработки персональных данных и меры по обеспечению безопасности персональных данных, предпринимаемые ООО «БИТФОРС».
|
||||
</p>
|
||||
<p>
|
||||
Оператор ставит своей важнейшей целью и условием осуществления своей деятельности соблюдение прав и свобод человека и гражданина при обработке его персональных данных, в том числе защиты права на неприкосновенность частной жизни, личную и семейную тайну.
|
||||
</p>
|
||||
<p>
|
||||
Настоящая Политика действует в отношении всех персональных данных, которые обрабатываются Оператором с использованием средств автоматизации и без использования таких средств.
|
||||
</p>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>1.4. Основные понятия</h4>
|
||||
<div className={styles.definitions}>
|
||||
<div className={styles.definition}>
|
||||
<strong>Автоматизированная обработка персональных данных</strong> — обработка персональных данных с помощью средств вычислительной техники.
|
||||
</div>
|
||||
<div className={styles.definition}>
|
||||
<strong>Обработка персональных данных</strong> — любое действие или совокупность действий, совершаемых с использованием средств автоматизации или без использования таких средств с персональными данными, включая сбор, запись, систематизацию, накопление, хранение, уточнение, извлечение, использование, передачу, обезличивание, блокирование, удаление, уничтожение.
|
||||
</div>
|
||||
<div className={styles.definition}>
|
||||
<strong>Оператор</strong> — юридическое или физическое лицо, организующие и осуществляющие обработку персональных данных.
|
||||
</div>
|
||||
<div className={styles.definition}>
|
||||
<strong>Персональные данные</strong> — любая информация, относящаяся к прямо или косвенно определенному или определяемому физическому лицу.
|
||||
</div>
|
||||
<div className={styles.definition}>
|
||||
<strong>Пользователь</strong> — любой посетитель веб-сайта https://bitforce-foundation.ru.
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>2. Сведения об операторе</h3>
|
||||
<ul className={styles.list}>
|
||||
<li>Полное наименование: Общество с ограниченной ответственностью «БИТФОРС»</li>
|
||||
<li>Сокращенное наименование: ООО «БИТФОРС»</li>
|
||||
<li>ИНН: 9810001062</li>
|
||||
<li>ОГРН: 1257800060990</li>
|
||||
<li>Юридический адрес: 196246, город Санкт-Петербург, Московское шоссе, дом 25, корпус 1, литера В, помещение 3-н</li>
|
||||
<li>Электронная почта: company@bitforcefoundation.ru</li>
|
||||
<li>Веб-сайт: https://bitforce-foundation.ru</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>3. Общие цели обработки персональных данных</h3>
|
||||
<h4 className={styles.subSectionTitle}>3.1.1. Основная деятельность:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Предоставление услуг по конвертации иного имущества</li>
|
||||
<li>Осуществление операций на криптовалютных рынках</li>
|
||||
<li>Предоставление услуг в области блокчейн технологий</li>
|
||||
<li>Обеспечение функционирования интернет-платформы и мобильных приложений</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>3.1.2. Обеспечение безопасности:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Предотвращение мошенничества и отмывания денежных средств</li>
|
||||
<li>Обеспечение безопасности платежных операций</li>
|
||||
<li>Выполнение требований по противодействию легализации доходов</li>
|
||||
<li>Идентификация и верификация клиентов</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>3.1.3. Соблюдение законодательства:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Исполнение требований российского и международного законодательства</li>
|
||||
<li>Взаимодействие с контролирующими и правоохранительными органами</li>
|
||||
<li>Ведение обязательной отчетности и документооборота</li>
|
||||
<li>Соблюдение налогового законодательства</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>4. Цели сбора персональных данных</h3>
|
||||
<div className={styles.goalsList}>
|
||||
<div className={styles.goal}>
|
||||
<strong>Регистрация и идентификация пользователей:</strong>
|
||||
<ul>
|
||||
<li>Создание учетной записи на веб-сайте</li>
|
||||
<li>Верификация личности в соответствии с требованиями законодательства</li>
|
||||
<li>Подтверждение права на осуществление операций</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className={styles.goal}>
|
||||
<strong>Обработка платежей и финансовых операций:</strong>
|
||||
<ul>
|
||||
<li>Осуществление операций по конвертации криптовалют</li>
|
||||
<li>Проведение расчетов и переводов денежных средств</li>
|
||||
<li>Ведение учета и истории транзакций</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className={styles.goal}>
|
||||
<strong>Коммуникация с клиентами:</strong>
|
||||
<ul>
|
||||
<li>Предоставление технической поддержки</li>
|
||||
<li>Уведомления о состоянии операций и счетов</li>
|
||||
<li>Информирование об изменениях в условиях предоставления услуг</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>5. Правовые основания обработки персональных данных</h3>
|
||||
<h4 className={styles.subSectionTitle}>5.1.1. Согласие субъекта персональных данных:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Обработка персональных данных в маркетинговых целях</li>
|
||||
<li>Использование файлов cookie и метрик</li>
|
||||
<li>Персонализация сервисов и предложений</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>5.1.2. Необходимость исполнения договора:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Регистрация и ведение учетных записей пользователей</li>
|
||||
<li>Осуществление финансовых операций и переводов</li>
|
||||
<li>Предоставление доступа к платформе и сервисам</li>
|
||||
<li>Техническая поддержка и обслуживание клиентов</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>5.1.3. Соблюдение правовой обязанности:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Выполнение требований валютного законодательства</li>
|
||||
<li>Противодействие легализации доходов, полученных преступным путем</li>
|
||||
<li>Соблюдение требований по налоговому учету и отчетности</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>6. Объем и категории обрабатываемых персональных данных</h3>
|
||||
<h4 className={styles.subSectionTitle}>6.1.1. Пользователи веб-сайта и мобильного приложения:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Зарегистрированные пользователи</li>
|
||||
<li>Посетители сайта без регистрации</li>
|
||||
<li>Потенциальные клиенты</li>
|
||||
<li>Бывшие клиенты</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>6.2.1. Идентификационные данные:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Фамилия, имя, отчество</li>
|
||||
<li>Дата рождения</li>
|
||||
<li>Гражданство</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>6.2.3. Контактная информация:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Номера телефонов (мобильный, домашний, рабочий)</li>
|
||||
<li>Адреса электронной почты</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>6.2.4. Финансовая информация:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Номера банковских счетов и карт</li>
|
||||
<li>Реквизиты кошельков криптовалют</li>
|
||||
<li>История операций и транзакций</li>
|
||||
<li>Данные о доходах и источниках средств</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>6.2.5. Техническая информация:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>IP-адреса устройств</li>
|
||||
<li>Данные о браузере и операционной системе</li>
|
||||
<li>Файлы cookie и локальное хранилище</li>
|
||||
<li>Логи действий на сайте</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>7. Порядок и условия обработки персональных данных</h3>
|
||||
<h4 className={styles.subSectionTitle}>7.1. Принципы обработки персональных данных:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Обработка осуществляется на законной и справедливой основе</li>
|
||||
<li>Обработка ограничивается достижением конкретных, заранее определенных целей</li>
|
||||
<li>Содержание и объем данных соответствуют заявленным целям</li>
|
||||
<li>Обрабатываемые персональные данные являются точными и актуальными</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>7.4. Сроки обработки персональных данных:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Персональные данные обрабатываются в течение времени, необходимого для достижения целей</li>
|
||||
<li>После достижения целей персональные данные подлежат уничтожению или обезличиванию</li>
|
||||
<li>Сроки хранения определяются требованиями законодательства</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>7.5. Места обработки персональных данных:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Основные серверы и хранилища данных расположены на территории Российской Федерации</li>
|
||||
<li>Резервные копии могут храниться в дата-центрах на территории РФ</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>8. Актуализация, исправление, удаление и уничтожение персональных данных</h3>
|
||||
<h4 className={styles.subSectionTitle}>8.2.2. Процедура исправления:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Рассмотрение запроса в течение 30 дней</li>
|
||||
<li>Проверка обоснованности требования об исправлении</li>
|
||||
<li>Внесение изменений во все информационные системы</li>
|
||||
<li>Уведомление субъекта о проведенных исправлениях</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>8.3.2. Процедура удаления:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Проверка наличия законных оснований для продолжения обработки</li>
|
||||
<li>Удаление из всех информационных систем и баз данных</li>
|
||||
<li>Удаление резервных копий (кроме архивных)</li>
|
||||
<li>Уведомление субъекта о выполненном удалении</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>9. Ответы на запросы субъектов персональных данных</h3>
|
||||
<h4 className={styles.subSectionTitle}>9.1.1. Право на информацию:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Подтверждение факта обработки персональных данных</li>
|
||||
<li>Правовые основания и цели обработки</li>
|
||||
<li>Применяемые способы обработки</li>
|
||||
<li>Наименование и местонахождение оператора</li>
|
||||
<li>Лица, имеющие доступ к персональным данным</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>9.2.2. Сроки рассмотрения:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Срок рассмотрения запроса составляет 30 дней с момента получения</li>
|
||||
<li>Срок может быть продлен на 30 дней при большом объеме информации</li>
|
||||
<li>О продлении срока субъект уведомляется в течение 30 дней</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>9.4. Плата за предоставление информации:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Первый запрос в течение года обрабатывается бесплатно</li>
|
||||
<li>За повторные запросы может взиматься плата в размере расходов</li>
|
||||
<li>Субъект уведомляется о размере платы до предоставления информации</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>10. Обеспечение безопасности персональных данных</h3>
|
||||
<h4 className={styles.subSectionTitle}>10.1. Правовые меры:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Назначение ответственного за организацию обработки персональных данных</li>
|
||||
<li>Принятие локальных актов по вопросам обработки персональных данных</li>
|
||||
<li>Ознакомление работников с требованиями законодательства</li>
|
||||
<li>Применение мер ответственности за нарушение требований</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>10.3. Технические меры:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Предотвращение несанкционированного доступа к персональным данным</li>
|
||||
<li>Своевременное обнаружение фактов несанкционированного доступа</li>
|
||||
<li>Возможность незамедлительного восстановления персональных данных</li>
|
||||
<li>Постоянный контроль за обеспечением уровня защищенности</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>10.4. Конкретные технические решения:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Использование сертифицированных средств защиты информации</li>
|
||||
<li>Шифрование персональных данных при передаче и хранении</li>
|
||||
<li>Применение межсетевых экранов и систем обнаружения вторжений</li>
|
||||
<li>Резервное копирование и обеспечение отказоустойчивости</li>
|
||||
<li>Антивирусная защита и обновление программного обеспечения</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>12. Заключительные положения</h3>
|
||||
<h4 className={styles.subSectionTitle}>12.2. Жалобы и обращения:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Субъекты персональных данных могут обратиться к Оператору по вопросам обработки</li>
|
||||
<li>Жалобы рассматриваются в установленном законом порядке</li>
|
||||
<li>При неурегулировании разногласий возможно обращение в Роскомнадзор или суд</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>12.4. Контактная информация для обращений:</h4>
|
||||
<p className={styles.contacts}>
|
||||
Почтовый адрес: 196246, г. Санкт-Петербург, Московское ш., д. 25, к. 1, лит. В, пом. 3-н<br />
|
||||
Электронная почта: company@bitforcefoundation.ru
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
34
src/pages/profile/ui/IndividualFields.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import type { MeResponse } from '@features/auth'
|
||||
import { FormField } from '@shared/ui'
|
||||
import { ProfileSection } from '@widgets/profile'
|
||||
import styles from './ProfilePage.module.css'
|
||||
|
||||
interface Props {
|
||||
data: MeResponse
|
||||
fullName: string
|
||||
phone: string
|
||||
onPhoneChange: (value: string) => void
|
||||
onPhoneBlur: () => void
|
||||
}
|
||||
|
||||
export function IndividualFields({ data, fullName, phone, onPhoneChange, onPhoneBlur }: Props) {
|
||||
return (
|
||||
<>
|
||||
<ProfileSection title="Личные данные">
|
||||
<div className={styles.grid2}>
|
||||
<FormField label="Полное ФИО" value={fullName} placeholder="Например: Иванов Иван Иванович" readOnly />
|
||||
<FormField label="Адрес электронной почты" value={data.email ?? ''} type="email" icon="check" placeholder="example@mail.ru" readOnly />
|
||||
<FormField label="Серия и номер паспорта" value={data.passport_data ?? ''} placeholder="0000 000000" readOnly />
|
||||
<FormField label="Номер телефона" value={phone} onChange={onPhoneChange} onBlur={onPhoneBlur} type="tel" placeholder="+7 (999) 000-00-00" />
|
||||
</div>
|
||||
</ProfileSection>
|
||||
|
||||
<ProfileSection title="Верификация">
|
||||
<div className={styles.grid2}>
|
||||
<FormField label="ИНН" value={data.inn ?? ''} readOnly icon="lock" placeholder="000000000000" />
|
||||
<FormField label="ID аккаунта" value={data.id ?? ''} readOnly icon="lock" placeholder="ECSA-00000000" />
|
||||
</div>
|
||||
</ProfileSection>
|
||||
</>
|
||||
)
|
||||
}
|
||||
47
src/pages/profile/ui/LegalEntityFields.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import type { MeResponse } from '@features/auth'
|
||||
import { FormField } from '@shared/ui'
|
||||
import { ProfileSection } from '@widgets/profile'
|
||||
import styles from './ProfilePage.module.css'
|
||||
|
||||
interface Props {
|
||||
data: MeResponse
|
||||
}
|
||||
|
||||
// Legal-account fields. Organization data lives in the nested `legal_entity`
|
||||
// object; person-level fields are null on these accounts.
|
||||
// All read-only — organization data is managed admin-side, not by the user.
|
||||
export function LegalEntityFields({ data }: Props) {
|
||||
const le = data.legal_entity
|
||||
if (!le) return null
|
||||
|
||||
return (
|
||||
<>
|
||||
<ProfileSection title="Данные организации">
|
||||
<div className={styles.grid2}>
|
||||
<FormField label="Наименование" value={le.name ?? ''} placeholder="ООО «Ромашка»" readOnly />
|
||||
<FormField label="Краткое наименование" value={le.short_name ?? ''} placeholder="Ромашка" readOnly />
|
||||
<FormField label="ИНН" value={le.inn ?? ''} readOnly icon="lock" placeholder="000000000000" />
|
||||
<FormField label="ОГРН" value={le.ogrn ?? ''} placeholder="1027700132195" readOnly />
|
||||
<FormField label="КПП" value={le.kpp ?? ''} placeholder="770801001" readOnly />
|
||||
<FormField label="Адрес электронной почты" value={data.email ?? ''} type="email" icon="check" placeholder="org@mail.ru" readOnly />
|
||||
</div>
|
||||
</ProfileSection>
|
||||
|
||||
<ProfileSection title="Адреса">
|
||||
<div className={styles.grid2}>
|
||||
<FormField label="Юридический адрес" value={le.legal_address ?? ''} placeholder="г. Москва, ул. Тверская, д. 1" readOnly />
|
||||
<FormField label="Фактический адрес" value={le.actual_address ?? ''} placeholder="г. Москва, ул. Тверская, д. 1" readOnly />
|
||||
</div>
|
||||
</ProfileSection>
|
||||
|
||||
<ProfileSection title="Контакты и верификация">
|
||||
<div className={styles.grid2}>
|
||||
<FormField label="Контактное лицо" value={le.contact_person ?? ''} placeholder="Иванов Иван Иванович" readOnly />
|
||||
<FormField label="Контактный телефон" value={le.contact_phone ?? ''} type="tel" placeholder="+7 (999) 000-00-00" readOnly />
|
||||
<FormField label="Статус" value={le.status ?? ''} placeholder="active" readOnly />
|
||||
<FormField label="ID аккаунта" value={data.id ?? ''} readOnly icon="lock" placeholder="ECSA-00000000" />
|
||||
</div>
|
||||
</ProfileSection>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,16 +1,64 @@
|
||||
import { useMe } from '@features/auth'
|
||||
import { Button, FormField } from '@shared/ui'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { useMe, useUpdatePhone } from '@features/auth'
|
||||
import { usePortfolio, useWalletAddresses } from '@features/wallet'
|
||||
import { ROUTES } from '@shared/config/routes'
|
||||
import { Button, FormField, Notification } from '@shared/ui'
|
||||
import { WalletHeader } from '@widgets/wallet-header'
|
||||
import { ProfileAvatar, ProfileSection } from '@widgets/profile'
|
||||
import { IndividualFields } from './IndividualFields'
|
||||
import { LegalEntityFields } from './LegalEntityFields'
|
||||
import styles from './ProfilePage.module.css'
|
||||
|
||||
export function ProfilePage() {
|
||||
const { data } = useMe()
|
||||
const { data: portfolio, isLoading: isPortfolioLoading } = usePortfolio()
|
||||
const { data: walletAddresses } = useWalletAddresses()
|
||||
const updatePhone = useUpdatePhone()
|
||||
const navigate = useNavigate()
|
||||
|
||||
const [phone, setPhone] = useState('')
|
||||
const [savedPhone, setSavedPhone] = useState('')
|
||||
const [notification, setNotification] = useState<{ message: string; status: 'success' | 'error' } | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (data?.phone != null) {
|
||||
setPhone(data.phone)
|
||||
setSavedPhone(data.phone)
|
||||
}
|
||||
}, [data?.phone])
|
||||
|
||||
function handlePhoneChange(value: string) {
|
||||
setPhone(value.replace(/[^\d+\s()-]/g, ''))
|
||||
}
|
||||
|
||||
function handlePhoneBlur() {
|
||||
const next = phone.trim()
|
||||
if (next === savedPhone || updatePhone.isPending) return
|
||||
updatePhone.mutate(next, {
|
||||
onSuccess: () => {
|
||||
setSavedPhone(next)
|
||||
setNotification({ status: 'success', message: 'Номер телефона обновлён' })
|
||||
},
|
||||
onError: () => {
|
||||
setNotification({ status: 'error', message: 'Не удалось обновить номер телефона' })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const capitalize = (s: string | null) => (s ? s[0].toUpperCase() + s.slice(1).toLowerCase() : '')
|
||||
const fullName = data
|
||||
? [data.last_name, data.first_name, data.middle_name].filter(Boolean).join(' ')
|
||||
? [data.last_name, data.first_name, data.middle_name].filter(Boolean).map(capitalize).join(' ')
|
||||
: ''
|
||||
|
||||
const isLegal = !!data && data.account_type !== 'individual'
|
||||
const displayName = isLegal ? (data?.legal_entity?.name ?? '') : fullName
|
||||
|
||||
const userBalance =
|
||||
isPortfolioLoading || !portfolio || portfolio.totalUsd == null
|
||||
? '$—'
|
||||
: `$${portfolio.totalUsd.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`
|
||||
|
||||
return (
|
||||
<div className={styles.page}>
|
||||
<WalletHeader />
|
||||
@@ -23,28 +71,24 @@ export function ProfilePage() {
|
||||
<div className={styles.profileTop}>
|
||||
<ProfileAvatar />
|
||||
<div className={styles.userInfo}>
|
||||
<span className={styles.userName}>{fullName}</span>
|
||||
<span className={styles.userBalance}>$245.00</span>
|
||||
<span className={styles.userBalanceRub}>≈ 22 340,50 ₽</span>
|
||||
<span className={styles.userName}>{displayName}</span>
|
||||
<span className={styles.userBalance}>{userBalance}</span>
|
||||
{/* <span className={styles.userBalanceRub}>≈ 22 340,50 ₽</span> */}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.sections}>
|
||||
<ProfileSection title="Личные данные">
|
||||
<div className={styles.grid2}>
|
||||
<FormField label="Полное ФИО" value={fullName} placeholder="Например: Иванов Иван Иванович" />
|
||||
<FormField label="Адрес электронной почты" value={data?.email ?? ''} type="email" icon="check" placeholder="example@mail.ru" readOnly />
|
||||
<FormField label="Серия и номер паспорта" value={data?.passport_data ?? ''} placeholder="0000 000000" readOnly />
|
||||
<FormField label="Номер телефона" value={data?.phone ?? ''} type="tel" icon="check" placeholder="+7 (999) 000-00-00" readOnly />
|
||||
</div>
|
||||
</ProfileSection>
|
||||
|
||||
<ProfileSection title="Верификация">
|
||||
<div className={styles.grid2}>
|
||||
<FormField label="ИНН" value={data?.inn ?? ''} readOnly icon="lock" placeholder="000000000000" />
|
||||
<FormField label="ID аккаунта" value={data?.id ?? ''} readOnly icon="lock" placeholder="ECSA-00000000" />
|
||||
</div>
|
||||
</ProfileSection>
|
||||
{data && (isLegal ? (
|
||||
<LegalEntityFields data={data} />
|
||||
) : (
|
||||
<IndividualFields
|
||||
data={data}
|
||||
fullName={fullName}
|
||||
phone={phone}
|
||||
onPhoneChange={handlePhoneChange}
|
||||
onPhoneBlur={handlePhoneBlur}
|
||||
/>
|
||||
))}
|
||||
|
||||
<ProfileSection
|
||||
title="Безопасность"
|
||||
@@ -56,7 +100,16 @@ export function ProfilePage() {
|
||||
}
|
||||
>
|
||||
<div className={styles.grid1}>
|
||||
<FormField label="Адрес ERC-20" readOnly icon="lock" value={data?.erc20 ?? ''} placeholder="0x0000000000000000000000000000000000000000" />
|
||||
{walletAddresses?.map(({ chain, address }) => (
|
||||
<FormField
|
||||
key={chain}
|
||||
label={`Адрес ${chain}`}
|
||||
readOnly
|
||||
icon="lock"
|
||||
value={address}
|
||||
placeholder="—"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</ProfileSection>
|
||||
|
||||
@@ -66,11 +119,18 @@ export function ProfilePage() {
|
||||
<span className={styles.mnemonicIcon}>🔑</span>
|
||||
<span className={styles.mnemonicText}>Сид-фраза из 12 слов для восстановления кошелька</span>
|
||||
</div>
|
||||
<Button variant="danger">⚠ Показать мнемонику</Button>
|
||||
<Button variant="danger" onClick={() => navigate(ROUTES.SEED_PHRASE)}>⚠ Показать мнемонику</Button>
|
||||
</div>
|
||||
</ProfileSection>
|
||||
</div>
|
||||
</main>
|
||||
{notification && (
|
||||
<Notification
|
||||
status={notification.status}
|
||||
message={notification.message}
|
||||
onClose={() => setNotification(null)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
1
src/pages/publichnaya-oferta/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { PublichnayaOfertaPage } from './ui/PublichnayaOfertaPage'
|
||||
@@ -0,0 +1,89 @@
|
||||
.main {
|
||||
padding: 40px 20px;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.container {
|
||||
background: var(--bg-mid, #1b1547);
|
||||
padding: 40px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
color: var(--text-primary, #ffffff);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
color: var(--text-secondary, #b5b0cc);
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.sectionTitle {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 15px;
|
||||
color: var(--text-primary, #ffffff);
|
||||
border-bottom: 2px solid var(--interactive, #4a6dff);
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.definitions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.definition {
|
||||
padding: 15px;
|
||||
background: var(--glass-bg, rgba(255, 255, 255, 0.04));
|
||||
border-left: 4px solid var(--interactive, #4a6dff);
|
||||
border-radius: 4px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.requisites {
|
||||
padding: 20px;
|
||||
background: var(--glass-bg, rgba(255, 255, 255, 0.04));
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--glass-border, rgba(255, 255, 255, 0.08));
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.requisites p {
|
||||
margin: 5px 0;
|
||||
color: var(--text-primary, #ffffff);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.sectionTitle {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.requisites {
|
||||
padding: 15px;
|
||||
}
|
||||
}
|
||||
200
src/pages/publichnaya-oferta/ui/PublichnayaOfertaPage.tsx
Normal file
@@ -0,0 +1,200 @@
|
||||
import { Footer } from '@widgets/footer'
|
||||
import { Header } from '@widgets/header'
|
||||
import styles from './PublichnayaOfertaPage.module.css'
|
||||
|
||||
export function PublichnayaOfertaPage() {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<main className={styles.main}>
|
||||
<div className={styles.container}>
|
||||
<h1 className={styles.title}>ПУБЛИЧНЫЙ ДОГОВОР ОФЕРТЫ</h1>
|
||||
|
||||
<h2 className={styles.subtitle}>ООО БИТФОРС</h2>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>Агентский договор</h3>
|
||||
<p>
|
||||
Настоящая оферта на заключение агентского договора (далее – Оферта, Договор) является публичным предложением Общества с ограниченной ответственностью «БИТФОРС», заключить договор на условиях и в порядке, определенных настоящей Офертой.
|
||||
</p>
|
||||
<p>
|
||||
Акцепт оферты производится в соответствии с пунктом 2 статьи 437 Гражданского кодекса Российской Федерации и равносилен заключению агентского договора в письменной форме.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>Основные понятия и определения действующего договора</h3>
|
||||
<div className={styles.definitions}>
|
||||
<div className={styles.definition}>
|
||||
<strong>Агент</strong> – юридическое лицо или индивидуальный предприниматель, зарегистрированный на территории Российской Федерации, в установленном действующим законодательством порядке.
|
||||
</div>
|
||||
<div className={styles.definition}>
|
||||
<strong>Принципал</strong> – сторона агентского договора, по поручению которой агент осуществляет юридические и иные действия от своего имени, но за счет принципала либо от имени и за счет принципала.
|
||||
</div>
|
||||
<div className={styles.definition}>
|
||||
<strong>Агентский договор</strong> – соглашение, по которому агент обязуется за вознаграждение совершать по поручению принципала юридические и иные действия от своего имени, но за счет принципала либо от имени и за счет принципала в соответствии с п. 1 ст. 1005 Гражданского Кодекса Российской Федерации.
|
||||
</div>
|
||||
<div className={styles.definition}>
|
||||
<strong>Личный кабинета Агента</strong> – ресурс, размещенный на сайте Принципала, предназначенный для взаимодействия Агента и Принципала.
|
||||
</div>
|
||||
<div className={styles.definition}>
|
||||
<strong>Отчетный период</strong> – период для взаиморасчетов с Агентом, равный одному календарному кварталу с даты активации любой из услуг, предоставляемой Принципалу.
|
||||
</div>
|
||||
<div className={styles.definition}>
|
||||
<strong>Отчет о сумме начислений (Отчет)</strong> – отчет, формируемый в Личном кабинете Агента на основании данных систем учета Принципала.
|
||||
</div>
|
||||
<div className={styles.definition}>
|
||||
<strong>Оферта (Договор)</strong> – настоящий документ, который отражает предложение и намерение ООО «БИТФОРС» считать заключенным договор с лицом, которым будет принято предложение на условиях, изложенных ниже.
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>1. Акцепт оферты и заключение агентского договора</h3>
|
||||
<p>
|
||||
Акцепт настоящей Оферты и заключение Агентского договора осуществляется Принципалом в процессе регистрации в Личном кабинете Принципала (на сайте Агента), при прочтении текста настоящей Оферты, путем проставления специальной отметки (галочки) напротив фразы «Я ознакомился с Офертой и принимаю ее условия» и нажатия кнопки «Подписать».
|
||||
</p>
|
||||
<p>
|
||||
Особый порядок принятия условий Оферты путем проставления специальной отметки (галочки) определяется интерфейсом Личного кабинета Принципала. Принципал не может зарегистрироваться в Личном кабинете и получить к нему доступ без подтверждения принятия условий Оферты.
|
||||
</p>
|
||||
<p>
|
||||
Принимая Оферту, Принципал подтверждает, что прочел и полностью согласен с документами, размещенными на сайте в разделе, предназначенном для Принципала, которые являются неотъемлемой частью настоящей Оферты (Договора) и обязательны для исполнения Сторонами.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>2. Общие положения</h3>
|
||||
<p>
|
||||
Публикуемые на сайте Агента документы (формы, требования, правила и т.п.), устанавливающие порядок и условия выполнения действий, предусмотренных настоящим Договором, являются неотъемлемой частью настоящего Договора и обязательны для исполнения Сторонами. Принципал обязан использовать формы документов, утвержденных Агентом, и не вправе вносить в них какие-либо изменения или дополнения.
|
||||
</p>
|
||||
<p>
|
||||
Агент обязуется уведомлять Принципала обо всех изменениях в документах, связанных с исполнением настоящего Договора, путем направления электронных сообщений (через Личный кабинет или на электронную почту Принципала) или размещением уведомлений об изменениях на сайте Агентов в разделе, предназначенном для размещения объявлений.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>3. Предмет договора</h3>
|
||||
<p>
|
||||
По настоящему Договору Принципал поручает, а Агент принимает на себя обязательство совершать от имени и за счет Принципала указанные действия, а Принципал обязуется выплатить Агенту вознаграждение за совершенные действия.
|
||||
</p>
|
||||
<p>
|
||||
По настоящему Договору Агент совершает следующие действия:
|
||||
</p>
|
||||
<ul>
|
||||
<li>Консультирование Принципала об услугах Агента, включая, помимо прочего, порядок активации и оказания услуг, работу в Личном кабинете Принципала и иные дополнительные услуги, оказываемые Агентом;</li>
|
||||
<li>Совершение сделок и иных юридических действий Агентом от своего имени, но за счёт Принципала.</li>
|
||||
</ul>
|
||||
<p>
|
||||
Настоящий Договор действует на территории Российской Федерации и иного иностранного государства.
|
||||
</p>
|
||||
<p>
|
||||
Права и обязанности по сделкам, совершенным Агентом во исполнение настоящего Договора, возникают непосредственно у Принципала.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>4. Права и обязанности сторон</h3>
|
||||
<p>
|
||||
Агент обязуется совершать действия, составляющие предмет настоящего Договора, в соответствии с законными интересами Принципала, сообщать Принципалу по его требованию все сведения о ходе исполнения настоящего Договора, передавать Принципалу в течение 7 рабочих дней имущество, полученное по сделкам.
|
||||
</p>
|
||||
<p>
|
||||
Агент несет ответственность за сохранность документов и персональных данных, переданных ему Принципалом для исполнения настоящего Договора.
|
||||
</p>
|
||||
<p>
|
||||
Принципал обязан без промедления принять отчет Агента, все предоставленные им документы, обеспечить Агента документами и материалами, необходимыми для выполнения настоящего Договора, возместить Агенту понесенные расходы и выплатить обусловленное Договором агентское вознаграждение.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>5. Агентское вознаграждение и порядок оплаты</h3>
|
||||
<p>
|
||||
Сумма вознаграждения Агента по настоящему Договору составляет:
|
||||
</p>
|
||||
<ul>
|
||||
<li>8% от 5 000 до 30 000 рублей</li>
|
||||
<li>6% от 30 000 до 100 000 рублей</li>
|
||||
<li>4% от 100 000 до 600 000 рублей</li>
|
||||
</ul>
|
||||
<p>
|
||||
Вознаграждение выплачивается Агенту с момента подписания настоящего Договора об исполнении поручения Агентом от своего имени, но за счет Принципала.
|
||||
</p>
|
||||
<p>
|
||||
Принципал возмещает следующие расходы Агента в сумме не более 30 000 рублей на оплату банковских услуг и иных комиссий.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>6. Ответственность сторон</h3>
|
||||
<p>
|
||||
В случае нарушения Агентом сроков, установленных Договором для передачи Принципалу полученного имущества, Принципал вправе предъявить требование об уплате неустойки в размере 0,1% от непереданной суммы за каждый день просрочки.
|
||||
</p>
|
||||
<p>
|
||||
В случае нарушения Принципалом сроков уплаты вознаграждения или возмещения расходов, Агент вправе предъявить требование об уплате неустойки в размере 0,1% от не уплаченной в срок суммы за каждый день просрочки.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>7. Форс-мажор</h3>
|
||||
<p>
|
||||
Стороны освобождаются от ответственности за частичное или полное неисполнение обязательств по настоящему Договору, если это неисполнение явилось следствием возникших после заключения настоящего Договора обстоятельств непреодолимой силы.
|
||||
</p>
|
||||
<p>
|
||||
При наступлении форс-мажорных обстоятельств каждая Сторона должна без промедления известить о них в письменном виде другую Сторону с указанием характера обстоятельств и их влияния на исполнение обязательств.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>8. Конфиденциальность</h3>
|
||||
<p>
|
||||
Стороны принимают все необходимые меры для того, чтобы их сотрудники, агенты, правопреемники без предварительного согласия другой Стороны не информировали третьих лиц о конфиденциальной информации и персональных данных Сторон настоящего Договора.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>9. Изменение и прекращение договора</h3>
|
||||
<p>
|
||||
Настоящий договор вступает в силу с момента его подписания и действует до момента исполнения сторонами своих обязательств по настоящему договору.
|
||||
</p>
|
||||
<p>
|
||||
Настоящий Договор может быть изменен или прекращен по письменному соглашению Сторон, а также в других случаях, предусмотренных законодательством Российской Федерации.
|
||||
</p>
|
||||
<p>
|
||||
Принципал вправе в любое время отказаться от исполнения настоящего Договора путем направления письменного уведомления Агенту за 3 рабочих дня.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>10. Заключительные положения</h3>
|
||||
<p>
|
||||
Ни одна из сторон не вправе передавать свои права и обязанности по настоящему договору третьим лицам без согласия другой стороны.
|
||||
</p>
|
||||
<p>
|
||||
Сообщения Стороны могут направлять по факсу, электронной почте или другим способом связи при условии, что он позволяет достоверно установить, от кого исходило сообщение и кому оно адресовано.
|
||||
</p>
|
||||
<p>
|
||||
Споры, вытекающие из настоящего Договора, разрешаются в досудебном порядке. При неурегулировании возникших разногласий спор разрешается в Арбитражном суде г. Санкт–Петербурга и Ленинградской области с обязательным соблюдением претензионного порядка.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>Реквизиты сторон</h3>
|
||||
<div className={styles.requisites}>
|
||||
<p>Общество с ограниченной ответственностью «БИТФОРС»</p>
|
||||
<p>196246, г. Санкт-Петербург, Московский р-н, Московское шоссе, д.25к1 литера в, помещ. 3-Н</p>
|
||||
<p>ИНН / КПП: 9810001062 / 781001001</p>
|
||||
<p>ОГРН: 1257800060990</p>
|
||||
<p>ОКПО / ОКАТО / ОКТМО: 68342261 / 40284000000 / 40377000000</p>
|
||||
<p>Руководитель: Кленин Михаил Васильевич</p>
|
||||
<p>Электронная почта: company@bitforcefoundation.ru</p>
|
||||
<p>Наименование банка: ФИЛИАЛ "САНКТ-ПЕТЕРБУРГСКИЙ" АО "АЛЬФА-БАНК"</p>
|
||||
<p>Корреспондентский счет: 30101810600000000786</p>
|
||||
<p>БИК: 044030786</p>
|
||||
<p>Расчетный счет: 40702810632250004861</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
1
src/pages/reestr-pd-rkn/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { ReestryPage } from './ui/ReestryPage'
|
||||
115
src/pages/reestr-pd-rkn/ui/ReestryPage.module.css
Normal file
@@ -0,0 +1,115 @@
|
||||
.main {
|
||||
padding: 40px 20px;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.container {
|
||||
background: var(--bg-mid, #1b1547);
|
||||
padding: 40px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
color: var(--text-primary, #ffffff);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
color: var(--text-secondary, #b5b0cc);
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.sectionTitle {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 15px;
|
||||
color: var(--text-primary, #ffffff);
|
||||
border-bottom: 2px solid var(--interactive, #4a6dff);
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 16px;
|
||||
line-height: 1.8;
|
||||
color: var(--text-primary, #ffffff);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.info {
|
||||
font-size: 16px;
|
||||
line-height: 1.8;
|
||||
color: var(--text-primary, #ffffff);
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.linkBlock {
|
||||
text-align: center;
|
||||
padding: 30px;
|
||||
background: var(--glass-bg, rgba(255, 255, 255, 0.04));
|
||||
border-radius: 8px;
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.button {
|
||||
display: inline-block;
|
||||
padding: 15px 40px;
|
||||
background: var(--interactive, #4a6dff);
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
border-radius: 6px;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
border: 2px solid var(--interactive, #4a6dff);
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background: transparent;
|
||||
color: var(--interactive, #4a6dff);
|
||||
}
|
||||
|
||||
.operatorInfo {
|
||||
padding: 20px;
|
||||
background: var(--glass-bg, rgba(255, 255, 255, 0.04));
|
||||
border-left: 4px solid var(--interactive, #4a6dff);
|
||||
border-radius: 4px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.operatorInfo p {
|
||||
margin: 10px 0;
|
||||
color: var(--text-primary, #ffffff);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.linkBlock {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.button {
|
||||
padding: 12px 30px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
74
src/pages/reestr-pd-rkn/ui/ReestryPage.tsx
Normal file
@@ -0,0 +1,74 @@
|
||||
import { Footer } from '@widgets/footer'
|
||||
import { Header } from '@widgets/header'
|
||||
import styles from './ReestryPage.module.css'
|
||||
|
||||
export function ReestryPage() {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<main className={styles.main}>
|
||||
<div className={styles.container}>
|
||||
<h1 className={styles.title}>Реестр операторов персональных данных</h1>
|
||||
<h2 className={styles.subtitle}>ООО «БИТФОРС»</h2>
|
||||
|
||||
<section className={styles.section}>
|
||||
<p className={styles.description}>
|
||||
Информация об операторе персональных данных размещена в реестре операторов персональных данных Федеральной службы по надзору в сфере связи, информационных технологий и массовых коммуникаций (Роскомнадзор).
|
||||
</p>
|
||||
|
||||
<p className={styles.info}>
|
||||
Вы можете просмотреть информацию об операторе в реестре Роскомнадзора, перейдя по ссылке ниже:
|
||||
</p>
|
||||
|
||||
<div className={styles.linkBlock}>
|
||||
<a
|
||||
href="https://pd.rkn.gov.ru/operators-registry/operators-list/?act=search&name_full=%D0%91%D0%B8%D1%82%D1%84%D0%BE%D1%80%D1%81&inn=9810001062®n="
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={styles.button}
|
||||
>
|
||||
Открыть реестр Роскомнадзора
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<p className={styles.info}>
|
||||
Реестр содержит информацию об операторах персональных данных, включая сведения о целях и методах обработки персональных данных, а также меры по обеспечению безопасности персональных данных.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>Информация об операторе</h3>
|
||||
<div className={styles.operatorInfo}>
|
||||
<p>
|
||||
<strong>Наименование:</strong> ООО «БИТФОРС»
|
||||
</p>
|
||||
<p>
|
||||
<strong>ИНН:</strong> 9810001062
|
||||
</p>
|
||||
<p>
|
||||
<strong>ОГРН:</strong> 1257800060990
|
||||
</p>
|
||||
<p>
|
||||
<strong>Юридический адрес:</strong> 196246, город Санкт-Петербург, Московское шоссе, дом 25, корпус 1, литера В, помещение 3-н
|
||||
</p>
|
||||
<p>
|
||||
<strong>Контактная информация:</strong> company@bitforcefoundation.ru
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>О Роскомнадзоре</h3>
|
||||
<p>
|
||||
Федеральная служба по надзору в сфере связи, информационных технологий и массовых коммуникаций (Роскомнадзор) — это федеральный орган исполнительной власти, осуществляющий функции по контролю и надзору в области персональных данных.
|
||||
</p>
|
||||
<p>
|
||||
Роскомнадзор ведет реестр операторов персональных данных в соответствии с требованиями Федерального закона «О персональных данных». Реестр является открытой информационной системой и доступен всем заинтересованным лицам.
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
1
src/pages/register-test/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { RegisterTestPage } from './ui/RegisterTestPage'
|
||||
227
src/pages/register-test/ui/RegisterTestPage.module.css
Normal file
@@ -0,0 +1,227 @@
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--glass-bg);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 24px;
|
||||
padding: 32px;
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
.logo img {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.title {
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 24px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
/* Переключатель типа регистрации */
|
||||
.typeSwitch {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 4px;
|
||||
padding: 4px;
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 14px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.typeOption {
|
||||
appearance: none;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--text-secondary);
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
padding: 12px 16px;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
transition: background 0.18s ease, color 0.18s ease;
|
||||
}
|
||||
|
||||
.typeOption:hover {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.typeOptionActive {
|
||||
background: var(--interactive);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.twoCol {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 20px 24px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.leftCol {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.rightCol {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.codeHint {
|
||||
font-size: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Кнопка возврата на шаг ввода данных */
|
||||
.backButton {
|
||||
appearance: none;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--text-secondary);
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
padding: 0;
|
||||
margin-bottom: 16px;
|
||||
cursor: pointer;
|
||||
transition: color 0.18s ease;
|
||||
}
|
||||
|
||||
.backButton:hover {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
/* Документы для юридического лица */
|
||||
.documents {
|
||||
margin-top: 24px;
|
||||
padding: 20px;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.documentsTitle {
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.documentsSubtitle {
|
||||
font-size: 12px;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.documentsList {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.documentItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 12px 14px;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.documentName {
|
||||
font-size: 13px;
|
||||
color: var(--text-primary);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.attachButton {
|
||||
flex-shrink: 0;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: var(--interactive);
|
||||
padding: 8px 14px;
|
||||
border: 1px solid var(--interactive);
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
transition: background 0.18s ease, color 0.18s ease;
|
||||
}
|
||||
|
||||
.attachButton:hover {
|
||||
background: var(--interactive);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.fileInput {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #ff5a5a;
|
||||
font-size: 13px;
|
||||
margin-top: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.submitWrapper {
|
||||
margin-top: 28px;
|
||||
}
|
||||
|
||||
.legal {
|
||||
text-align: center;
|
||||
font-size: 11px;
|
||||
color: var(--text-secondary);
|
||||
margin-top: 20px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.legal a {
|
||||
color: var(--interactive);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.legal a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@media (max-width: 560px) {
|
||||
.card {
|
||||
padding: 32px 20px;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.twoCol {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.documentItem {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
155
src/pages/register-test/ui/RegisterTestPage.tsx
Normal file
@@ -0,0 +1,155 @@
|
||||
import { useState } from 'react'
|
||||
import { FormField, PrimaryButton, Button } from '@shared/ui'
|
||||
import logo from '@shared/assets/logo-full-white.png'
|
||||
import styles from './RegisterTestPage.module.css'
|
||||
|
||||
type AccountType = 'individual' | 'legal'
|
||||
type Step = 'info' | 'documents'
|
||||
|
||||
const LEGAL_DOCUMENTS = [
|
||||
'Свидетельство о государственной регистрации (ОГРН)',
|
||||
'Свидетельство о постановке на учёт в налоговом органе (ИНН)',
|
||||
'Устав организации (действующая редакция)',
|
||||
'Решение/протокол о назначении руководителя',
|
||||
'Документ, подтверждающий полномочия лица, открывающего счёт',
|
||||
'Карточка с образцами подписей и оттиска печати',
|
||||
]
|
||||
|
||||
export function RegisterTestPage() {
|
||||
const [email, setEmail] = useState('')
|
||||
const [password, setPassword] = useState('')
|
||||
const [confirmPassword, setConfirmPassword] = useState('')
|
||||
const [verificationCode, setVerificationCode] = useState('')
|
||||
|
||||
const [accountType, setAccountType] = useState<AccountType>('individual')
|
||||
const [step, setStep] = useState<Step>('info')
|
||||
const isLegal = accountType === 'legal'
|
||||
|
||||
// Тестовая страница — без проверок и запросов. «Создать» просто ведёт на шаг 2.
|
||||
const handleInfoSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
setStep('documents')
|
||||
}
|
||||
|
||||
const handleDocumentsSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
// Тестовая страница — отправки нет.
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.page}>
|
||||
{step === 'info' ? (
|
||||
<form className={styles.card} onSubmit={handleInfoSubmit}>
|
||||
<div className={styles.logo}>
|
||||
<img src={logo} alt="ЭКСА" />
|
||||
</div>
|
||||
<h1 className={styles.title}>Создать кошелёк ЭКСА</h1>
|
||||
|
||||
{/* Выбор типа регистрации — перед всеми полями */}
|
||||
<div className={styles.typeSwitch} role="tablist" aria-label="Тип регистрации">
|
||||
<button
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-selected={!isLegal}
|
||||
className={`${styles.typeOption} ${!isLegal ? styles.typeOptionActive : ''}`}
|
||||
onClick={() => setAccountType('individual')}
|
||||
>
|
||||
Физическое лицо
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
role="tab"
|
||||
aria-selected={isLegal}
|
||||
className={`${styles.typeOption} ${isLegal ? styles.typeOptionActive : ''}`}
|
||||
onClick={() => setAccountType('legal')}
|
||||
>
|
||||
Юридическое лицо
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className={styles.twoCol}>
|
||||
<div className={styles.leftCol}>
|
||||
<FormField
|
||||
label={isLegal ? 'Введите корпоративный email' : 'Введите адрес электронной почты'}
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={setEmail}
|
||||
placeholder={isLegal ? 'name@company.ru' : 'example@mail.ru'}
|
||||
/>
|
||||
<FormField
|
||||
label="Придумайте пароль"
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={setPassword}
|
||||
placeholder="••••••••"
|
||||
/>
|
||||
<FormField
|
||||
label="Повторите пароль"
|
||||
type="password"
|
||||
value={confirmPassword}
|
||||
onChange={setConfirmPassword}
|
||||
placeholder="••••••••"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.rightCol}>
|
||||
<Button variant="ghost" type="button">
|
||||
Получить проверочный код
|
||||
</Button>
|
||||
<span className={styles.codeHint}>Код не пришёл</span>
|
||||
<FormField
|
||||
label="Ввести код"
|
||||
type="text"
|
||||
value={verificationCode}
|
||||
onChange={setVerificationCode}
|
||||
placeholder="000 000"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.submitWrapper}>
|
||||
<PrimaryButton label="Создать" />
|
||||
</div>
|
||||
|
||||
<p className={styles.legal}>
|
||||
Нажимая «Создать», вы принимаете<br />
|
||||
<a href="#">Пользовательское соглашение</a> и <a href="#">Политику конфиденциальности</a>
|
||||
</p>
|
||||
</form>
|
||||
) : (
|
||||
/* Шаг 2: прикрепление документов (только юр. лицо) */
|
||||
<form className={styles.card} onSubmit={handleDocumentsSubmit}>
|
||||
<div className={styles.logo}>
|
||||
<img src={logo} alt="ЭКСА" />
|
||||
</div>
|
||||
|
||||
<button type="button" className={styles.backButton} onClick={() => setStep('info')}>
|
||||
← Назад к данным
|
||||
</button>
|
||||
|
||||
<h1 className={styles.title}>Прикрепите документы</h1>
|
||||
<p className={styles.documentsSubtitle}>
|
||||
Для открытия счёта юридическому лицу прикрепите сканы или фотографии
|
||||
следующих документов:
|
||||
</p>
|
||||
|
||||
<ul className={styles.documentsList}>
|
||||
{LEGAL_DOCUMENTS.map((doc) => (
|
||||
<li key={doc} className={styles.documentItem}>
|
||||
<span className={styles.documentName}>{doc}</span>
|
||||
<label className={styles.attachButton}>
|
||||
Прикрепить
|
||||
<input type="file" className={styles.fileInput} multiple />
|
||||
</label>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<div className={styles.submitWrapper}>
|
||||
<PrimaryButton label="Создать аккаунт" />
|
||||
</div>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
1
src/pages/restore-password/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { RestorePasswordPage } from './ui/RestorePasswordPage'
|
||||
@@ -0,0 +1,7 @@
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
10
src/pages/restore-password/ui/RestorePasswordPage.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import { RestorePasswordForm } from '@widgets/restore-password-form'
|
||||
import styles from './RestorePasswordPage.module.css'
|
||||
|
||||
export function RestorePasswordPage() {
|
||||
return (
|
||||
<div className={styles.page}>
|
||||
<RestorePasswordForm />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,16 +1,18 @@
|
||||
import { WalletHeader } from '@widgets/wallet-header'
|
||||
import { SeedPhraseWidget } from '@widgets/seed-phrase'
|
||||
import { useRevealMnemonic } from '@features/wallet'
|
||||
import styles from './SeedPhrasePage.module.css'
|
||||
|
||||
const MOCK_WORDS = ['egg', 'phone', 'long', 'vibe', 'potato', 'soup', 'skirt', 'black', 'phase', 'word', 'num', 'cucumber']
|
||||
|
||||
export function SeedPhrasePage() {
|
||||
const { data: mnemonic, isLoading } = useRevealMnemonic()
|
||||
const words = mnemonic ? mnemonic.split(' ') : []
|
||||
|
||||
return (
|
||||
<div className={styles.page}>
|
||||
<WalletHeader />
|
||||
<main className={styles.main}>
|
||||
<div className={styles.glow} />
|
||||
<SeedPhraseWidget words={MOCK_WORDS} />
|
||||
{!isLoading && <SeedPhraseWidget words={words} />}
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
|
||||
1
src/pages/soglasie-personalnyh-dannyh/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { SoglasiePage } from './ui/SoglasiePage'
|
||||
127
src/pages/soglasie-personalnyh-dannyh/ui/SoglasiePage.module.css
Normal file
@@ -0,0 +1,127 @@
|
||||
.main {
|
||||
padding: 40px 20px;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.container {
|
||||
background: var(--bg-mid, #1b1547);
|
||||
padding: 40px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
color: var(--text-primary, #ffffff);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
color: var(--text-secondary, #b5b0cc);
|
||||
}
|
||||
|
||||
.section {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.sectionTitle {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 15px;
|
||||
color: var(--text-primary, #ffffff);
|
||||
border-bottom: 2px solid var(--interactive, #4a6dff);
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.subSectionTitle {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 10px;
|
||||
color: var(--text-primary, #ffffff);
|
||||
}
|
||||
|
||||
.list {
|
||||
list-style: disc;
|
||||
margin-left: 20px;
|
||||
line-height: 1.8;
|
||||
color: var(--text-primary, #ffffff);
|
||||
}
|
||||
|
||||
.list li {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.list strong {
|
||||
color: var(--text-primary, #ffffff);
|
||||
}
|
||||
|
||||
.info {
|
||||
padding: 20px;
|
||||
background: var(--glass-bg, rgba(255, 255, 255, 0.04));
|
||||
border-left: 4px solid var(--interactive, #4a6dff);
|
||||
border-radius: 4px;
|
||||
margin: 15px 0;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.info p {
|
||||
margin: 8px 0;
|
||||
color: var(--text-primary, #ffffff);
|
||||
}
|
||||
|
||||
.contacts {
|
||||
padding: 20px;
|
||||
background: var(--glass-bg, rgba(255, 255, 255, 0.04));
|
||||
border-left: 4px solid var(--interactive, #4a6dff);
|
||||
border-radius: 4px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.contacts p {
|
||||
margin: 8px 0;
|
||||
color: var(--text-primary, #ffffff);
|
||||
}
|
||||
|
||||
.confirmation {
|
||||
padding: 10px 0;
|
||||
color: var(--text-primary, #ffffff);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.sectionTitle {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.subSectionTitle {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.list {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.info,
|
||||
.contacts {
|
||||
padding: 15px;
|
||||
}
|
||||
}
|
||||
300
src/pages/soglasie-personalnyh-dannyh/ui/SoglasiePage.tsx
Normal file
@@ -0,0 +1,300 @@
|
||||
import { Footer } from '@widgets/footer'
|
||||
import { Header } from '@widgets/header'
|
||||
import styles from './SoglasiePage.module.css'
|
||||
|
||||
export function SoglasiePage() {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<main className={styles.main}>
|
||||
<div className={styles.container}>
|
||||
<h1 className={styles.title}>СОГЛАСИЕ НА ОБРАБОТКУ ПЕРСОНАЛЬНЫХ ДАННЫХ</h1>
|
||||
<h2 className={styles.subtitle}>ООО «БИТФОРС»</h2>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>Преамбула</h3>
|
||||
<p>
|
||||
Я, субъект персональных данных, действуя своей волей и в своем интересе, в соответствии с требованиями Федерального закона от 27.07.2006 № 152-ФЗ «О персональных данных», предоставляю ООО «БИТФОРС» согласие на обработку моих персональных данных на условиях и для целей, определенных настоящим Согласием.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>1. Сведения об операторе</h3>
|
||||
<div className={styles.info}>
|
||||
<p>Полное наименование: Общество с ограниченной ответственностью «БИТФОРС»</p>
|
||||
<p>ИНН: 9810001062</p>
|
||||
<p>ОГРН: 1257800060990</p>
|
||||
<p>Юридический адрес: 196246, город Санкт-Петербург, Московское шоссе, дом 25, корпус 1, литера В, помещение 3-н</p>
|
||||
<p>Электронная почта: company@bitforcefoundation.ru</p>
|
||||
<p>Веб-сайт: https://bitforce-foundation.ru</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>2. Правовые основания обработки</h3>
|
||||
<p>
|
||||
Настоящее согласие предоставляется на основании пункта 1 части 1 статьи 6 Федерального закона «О персональных данных» и является правовым основанием для обработки персональных данных Оператором.
|
||||
</p>
|
||||
<p>Согласие дается добровольно, своей волей и в своих интересах.</p>
|
||||
<p>
|
||||
Субъект персональных данных понимает последствия предоставления согласия, включая возможные риски, связанные с обработкой персональных данных.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>3. Цели обработки персональных данных</h3>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>3.1. Основные цели:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Регистрация и ведение учетной записи на веб-сайте и в мобильном приложении</li>
|
||||
<li>Идентификация и верификация личности в соответствии с требованиями законодательства</li>
|
||||
<li>Предоставление услуг по обмену криптовалют и электронных денежных средств</li>
|
||||
<li>Проведение финансовых операций, переводов и расчетов</li>
|
||||
<li>Ведение учета и истории операций</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>3.2. Дополнительные цели:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Обеспечение безопасности операций и предотвращение мошенничества</li>
|
||||
<li>Выполнение требований по противодействию легализации доходов</li>
|
||||
<li>Соблюдение требований валютного, налогового и иного применимого законодательства</li>
|
||||
<li>Предоставление технической поддержки и клиентского сервиса</li>
|
||||
<li>Рассылка уведомлений о состоянии операций и изменениях в условиях</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>3.3. Маркетинговые цели (при дополнительном согласии):</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Направление информационных и рекламных материалов</li>
|
||||
<li>Проведение маркетинговых исследований и опросов</li>
|
||||
<li>Персонализация предложений и услуг</li>
|
||||
<li>Анализ предпочтений и поведения для улучшения сервисов</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>3.4. Аналитические цели:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Анализ использования веб-сайта и мобильного приложения</li>
|
||||
<li>Улучшение качества предоставляемых услуг</li>
|
||||
<li>Разработка новых продуктов и сервисов</li>
|
||||
<li>Создание статистических отчетов в обезличенном виде</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>4. Перечень персональных данных</h3>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>4.1. Идентификационные данные:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Фамилия, имя, отчество</li>
|
||||
<li>Дата рождения</li>
|
||||
<li>Гражданство</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>4.2. Документы, удостоверяющие личность:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Серия и номер паспорта гражданина Российской Федерации</li>
|
||||
<li>Дата выдачи и код подразделения</li>
|
||||
<li>Адрес регистрации по месту жительства</li>
|
||||
<li>Цифровые копии (сканы) документов</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>4.3. Контактная информация:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Номера телефонов (мобильный, домашний, рабочий)</li>
|
||||
<li>Адреса электронной почты</li>
|
||||
<li>Почтовые адреса (фактического проживания, для корреспонденции)</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>4.4. Финансовая информация:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Номера банковских счетов и реквизиты банковских карт</li>
|
||||
<li>Реквизиты криптовалютных кошельков и адресов</li>
|
||||
<li>Информация о доходах и источниках происхождения денежных средств</li>
|
||||
<li>История финансовых операций и транзакций</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>4.5. Техническая информация:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>IP-адреса устройств, с которых осуществляется доступ к сервисам</li>
|
||||
<li>Информация о браузере, операционной системе и устройстве</li>
|
||||
<li>Файлы cookie и данные локального хранилища</li>
|
||||
<li>Логи действий и история использования сервисов</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>4.6. Дополнительная информация:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Фотографии для процедур верификации</li>
|
||||
<li>Видеозаписи процедур видеоидентификации</li>
|
||||
<li>Биометрические данные (при использовании соответствующих технологий)</li>
|
||||
<li>Информация о семейном положении и профессиональной деятельности</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>5. Перечень действий с персональными данными</h3>
|
||||
<p>Согласие распространяется на следующие действия (операции) с персональными данными:</p>
|
||||
<ul className={styles.list}>
|
||||
<li>Сбор, запись и первичная обработка персональных данных</li>
|
||||
<li>Накопление и систематизация в базах данных</li>
|
||||
<li>Создание резервных копий и архивирование</li>
|
||||
<li>Извлечение, использование и анализ данных</li>
|
||||
<li>Уточнение, обновление и актуализация информации</li>
|
||||
<li>Передача данных третьим лицам</li>
|
||||
<li>Обезличивание и удаление данных</li>
|
||||
<li>Автоматизированная обработка и профилирование</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>6. Лица, которым могут быть переданы персональные данные</h3>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>6.1. Сотрудники Оператора:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Уполномоченные сотрудники, непосредственно участвующие в обработке</li>
|
||||
<li>Сотрудники службы безопасности и комплаенса</li>
|
||||
<li>Сотрудники технической поддержки</li>
|
||||
<li>Руководящий состав в рамках их полномочий</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>6.2. Государственные и муниципальные органы:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Федеральная служба по финансовому мониторингу</li>
|
||||
<li>Федеральная налоговая служба</li>
|
||||
<li>Правоохранительные органы (при наличии законных требований)</li>
|
||||
<li>Суды и органы исполнения судебных решений</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>6.3. Партнеры и контрагенты:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Банки и платежные системы</li>
|
||||
<li>Операторы электронных денежных средств</li>
|
||||
<li>Поставщики технологических решений</li>
|
||||
<li>Аудиторские и консалтинговые организации</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>7. Сроки обработки персональных данных</h3>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>7.1. Общие принципы:</h4>
|
||||
<p>
|
||||
Персональные данные обрабатываются в течение времени, необходимого для достижения целей обработки. После достижения целей данные подлежат уничтожению или обезличиванию.
|
||||
</p>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>7.2. Конкретные сроки обработки:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>
|
||||
<strong>Данные активных клиентов:</strong> в течение всего периода отношений плюс 5 лет после прекращения
|
||||
</li>
|
||||
<li>
|
||||
<strong>Данные для идентификации:</strong> 5 лет с момента прекращения отношений
|
||||
</li>
|
||||
<li>
|
||||
<strong>Финансовая информация:</strong> 5 лет с даты совершения операции
|
||||
</li>
|
||||
<li>
|
||||
<strong>Маркетинговые данные:</strong> до отзыва согласия, но не более 3 лет
|
||||
</li>
|
||||
<li>
|
||||
<strong>Техническая информация:</strong> 1 год для безопасности, 6 месяцев для логов
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>8. Права субъекта персональных данных</h3>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>8.1. Право на информацию:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Получение подтверждения факта обработки персональных данных</li>
|
||||
<li>Получение информации о целях и способах обработки</li>
|
||||
<li>Информация о сроках обработки и составе данных</li>
|
||||
<li>Сведения о лицах, которым передаются данные</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>8.2. Право на доступ:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Получение копий обрабатываемых персональных данных</li>
|
||||
<li>Ознакомление с историей обработки и изменений</li>
|
||||
<li>Получение информации об источниках персональных данных</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>8.3. Право на исправление и удаление:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Требование исправления неточных или неполных данных</li>
|
||||
<li>Требование удаления персональных данных при наличии оснований</li>
|
||||
<li>Удаление данных после отзыва согласия</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>8.4. Право на отзыв согласия:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Согласие может быть отозвано в любое время</li>
|
||||
<li>Отзыв оформляется в письменной форме</li>
|
||||
<li>После отзыва обработка прекращается в разумные сроки</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>8.5. Право на обжалование:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Обращение к Оператору с жалобами на действия по обработке данных</li>
|
||||
<li>Обращение в Роскомнадзор или его территориальные органы</li>
|
||||
<li>Обращение в суд для защиты нарушенных прав</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>8.6. Порядок реализации прав:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Обращения направляются на адрес: company@bitforcefoundation.ru</li>
|
||||
<li>Обращения рассматриваются в течение 30 дней</li>
|
||||
<li>При необходимости срок может быть продлен на 30 дней</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section className={styles.section}>
|
||||
<h3 className={styles.sectionTitle}>9. Заключительные положения</h3>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>9.1. Действие согласия:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Согласие действует с момента его предоставления</li>
|
||||
<li>Согласие действует до его отзыва или до достижения целей обработки</li>
|
||||
<li>При существенных изменениях целей требуется новое согласие</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>9.2. Форма предоставления согласия:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Согласие может быть предоставлено в письменной форме</li>
|
||||
<li>Согласие может быть предоставлено в электронной форме</li>
|
||||
<li>Согласие может выражаться путем совершения конклюдентных действий</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>9.3. Последствия непредоставления согласия:</h4>
|
||||
<ul className={styles.list}>
|
||||
<li>Отказ в предоставлении согласия может повлечь невозможность регистрации</li>
|
||||
<li>Отказ может ограничить доступ к отдельным услугам</li>
|
||||
<li>Отказ в согласии на маркетинг не влияет на основные услуги</li>
|
||||
<li>Субъект вправе предоставить частичное согласие</li>
|
||||
</ul>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>9.4. Контактная информация:</h4>
|
||||
<div className={styles.contacts}>
|
||||
<p>Почтовый адрес: 196246, г. Санкт-Петербург, Московское ш., д. 25, к. 1, лит. В, пом. 3-н</p>
|
||||
<p>Электронная почта: company@bitforcefoundation.ru</p>
|
||||
<p>Ответственное лицо: Кленин Михаил Васильевич</p>
|
||||
<p>Официальный сайт: https://bitforce-foundation.ru</p>
|
||||
</div>
|
||||
|
||||
<h4 className={styles.subSectionTitle}>9.5. Подтверждение понимания:</h4>
|
||||
<p className={styles.confirmation}>
|
||||
Предоставляя настоящее согласие, я подтверждаю, что:
|
||||
</p>
|
||||
<ul className={styles.list}>
|
||||
<li>Ознакомлен с содержанием согласия и понимаю его значение</li>
|
||||
<li>Понимаю цели и способы обработки моих персональных данных</li>
|
||||
<li>Знаю о своих правах и способах их реализации</li>
|
||||
<li>Согласие предоставляется добровольно и осознанно</li>
|
||||
<li>Имею возможность отозвать согласие в любое время</li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,52 +1,12 @@
|
||||
.page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
background: var(--bg-deep);
|
||||
}
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding: 24px 28px 0;
|
||||
}
|
||||
|
||||
.tab {
|
||||
padding: 10px 24px;
|
||||
border-radius: 10px;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
font-family: var(--font-sans);
|
||||
letter-spacing: 0.5px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.active {
|
||||
background: linear-gradient(135deg, var(--grad-edge), var(--grad-center));
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.inactive {
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.inactive:hover {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 32px 20px 48px;
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 32px 20px 48px;
|
||||
}
|
||||
|
||||
@media (max-width: 650px) {
|
||||
.main {
|
||||
padding: 32px 20px;
|
||||
}
|
||||
.content {
|
||||
padding: 32px 20px;
|
||||
}
|
||||
}
|
||||
@@ -1,38 +1,14 @@
|
||||
import { useState } from 'react'
|
||||
import { Footer } from '@widgets/footer'
|
||||
import { SwapForm } from '@widgets/swap-form'
|
||||
import { WalletHeader } from '@widgets/wallet-header'
|
||||
import { SwapBridgeTabs } from '@widgets/swap-bridge-tabs'
|
||||
import styles from './SwapPage.module.css'
|
||||
|
||||
type Tab = 'swap' | 'bridge'
|
||||
|
||||
export function SwapPage() {
|
||||
const [tab, setTab] = useState<Tab>('swap')
|
||||
|
||||
return (
|
||||
<div className={styles.page}>
|
||||
<WalletHeader />
|
||||
|
||||
<div className={styles.tabs}>
|
||||
<button
|
||||
className={`${styles.tab} ${tab === 'swap' ? styles.active : styles.inactive}`}
|
||||
onClick={() => setTab('swap')}
|
||||
>
|
||||
СВОП
|
||||
</button>
|
||||
<button
|
||||
className={`${styles.tab} ${tab === 'bridge' ? styles.active : styles.inactive}`}
|
||||
onClick={() => setTab('bridge')}
|
||||
>
|
||||
БРИДЖ
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<main className={styles.main}>
|
||||
<>
|
||||
<SwapBridgeTabs active="swap" />
|
||||
<div className={styles.content}>
|
||||
<SwapForm />
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
1
src/pages/transactions/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { TransactionsPage } from './ui/TransactionsPage'
|
||||
74
src/pages/transactions/ui/TransactionsPage.module.css
Normal file
@@ -0,0 +1,74 @@
|
||||
.inner {
|
||||
padding: 28px 32px 40px;
|
||||
max-width: 1200px;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.glow {
|
||||
position: absolute;
|
||||
top: -40px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 600px;
|
||||
height: 320px;
|
||||
background: radial-gradient(ellipse, rgba(61, 42, 142, 0.15), transparent 70%);
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
margin: 0 0 24px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin: 0 0 20px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.tab {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 38px;
|
||||
padding: 0 20px;
|
||||
background: transparent;
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
border-radius: 10px;
|
||||
color: var(--text-secondary);
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
font-family: inherit;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s, border-color 0.15s, color 0.15s;
|
||||
}
|
||||
|
||||
.tab:hover {
|
||||
border-color: rgba(74, 109, 255, 0.4);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.tab[data-active] {
|
||||
background: rgba(74, 109, 255, 0.12);
|
||||
border-color: rgba(74, 109, 255, 0.5);
|
||||
color: var(--interactive, #4a6dff);
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.inner {
|
||||
padding: 20px 16px 32px;
|
||||
}
|
||||
|
||||
.glow {
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
45
src/pages/transactions/ui/TransactionsPage.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import { useState } from 'react'
|
||||
import { useMe } from '@features/auth'
|
||||
import { TransactionsList } from '@widgets/transactions-list'
|
||||
import { PurchaseRequestsList } from '@widgets/purchase-requests-list'
|
||||
import styles from './TransactionsPage.module.css'
|
||||
|
||||
type Tab = 'transactions' | 'requests'
|
||||
|
||||
export function TransactionsPage() {
|
||||
const { data } = useMe()
|
||||
const [tab, setTab] = useState<Tab>('transactions')
|
||||
|
||||
const isLegal = !!data && data.account_type !== 'individual'
|
||||
const activeTab: Tab = isLegal ? tab : 'transactions'
|
||||
|
||||
return (
|
||||
<div className={styles.inner}>
|
||||
<div className={styles.glow} />
|
||||
<h1 className={styles.title}>Транзакции</h1>
|
||||
|
||||
{isLegal && (
|
||||
<div className={styles.tabs}>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.tab}
|
||||
data-active={activeTab === 'transactions' || undefined}
|
||||
onClick={() => setTab('transactions')}
|
||||
>
|
||||
Транзакции
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.tab}
|
||||
data-active={activeTab === 'requests' || undefined}
|
||||
onClick={() => setTab('requests')}
|
||||
>
|
||||
Заявки
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === 'transactions' ? <TransactionsList /> : <PurchaseRequestsList />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -35,6 +35,20 @@
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.noWallet {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 24px;
|
||||
min-height: 300px;
|
||||
color: var(--text-primary);
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.glow {
|
||||
width: auto;
|
||||
|
||||
@@ -1,25 +1,53 @@
|
||||
import { Navigate } from 'react-router-dom'
|
||||
import { Navigate, useNavigate, useParams } from 'react-router-dom'
|
||||
import { useMe } from '@features/auth'
|
||||
import { ROUTES } from '@shared/config/routes'
|
||||
import { usePortfolio, useCreateWallet, CHAINS, type Chain } from '@features/wallet'
|
||||
import { BalanceCard } from '@widgets/balance-card'
|
||||
import { TokenTable } from '@widgets/token-table'
|
||||
import { TokenTable, AllTokenTable } from '@widgets/token-table'
|
||||
import { WalletHeader } from '@widgets/wallet-header'
|
||||
import { WalletChainTabs } from '@widgets/wallet-chain-tabs'
|
||||
import { Button } from '@shared/ui'
|
||||
import styles from './WalletPage.module.css'
|
||||
|
||||
export function WalletPage() {
|
||||
const { data, isLoading, isError } = useMe()
|
||||
const { error: portfolioError } = usePortfolio()
|
||||
const { mutate: createWallet, isPending } = useCreateWallet()
|
||||
const navigate = useNavigate()
|
||||
const { chain: chainParam } = useParams<{ chain?: string }>()
|
||||
|
||||
const noWallet = (portfolioError as { error?: string } | null)?.error?.includes('No wallets')
|
||||
|
||||
if (isLoading) return null
|
||||
if (isError) return <div className={styles.error}>Произошла ошибка. Попробуйте обновить страницу.</div>
|
||||
if (data && !data.kyc_verified) return <Navigate to={ROUTES.KYC} replace />
|
||||
|
||||
const upper = chainParam?.toUpperCase() as Chain | undefined
|
||||
const chain: Chain | undefined = upper && CHAINS.includes(upper) ? upper : undefined
|
||||
|
||||
return (
|
||||
<div className={styles.page}>
|
||||
<WalletHeader />
|
||||
<main className={styles.main}>
|
||||
<div className={styles.glow} />
|
||||
<BalanceCard />
|
||||
<TokenTable />
|
||||
{noWallet ? (
|
||||
<div className={styles.noWallet}>
|
||||
<p>У вас пока нет кошелька. Создайте его, чтобы начать.</p>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => createWallet(undefined, { onSuccess: () => navigate(ROUTES.SEED_PHRASE) })}
|
||||
disabled={isPending}
|
||||
>
|
||||
{isPending ? 'Создание...' : 'Создать кошелёк'}
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<BalanceCard />
|
||||
<WalletChainTabs />
|
||||
{chain ? <TokenTable chain={chain} /> : <AllTokenTable />}
|
||||
</>
|
||||
)}
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -6,17 +6,28 @@ interface CsrfResponse {
|
||||
}
|
||||
|
||||
let cachedToken: string | null = null
|
||||
let inflight: Promise<string> | null = null
|
||||
|
||||
export function clearCsrfCache(): void {
|
||||
cachedToken = null
|
||||
inflight = null
|
||||
}
|
||||
|
||||
export async function getCsrfToken(): Promise<string> {
|
||||
if (cachedToken) return cachedToken
|
||||
const res = await fetch(`${API_URL}/csrf/token`, {
|
||||
credentials: 'include',
|
||||
})
|
||||
const data: CsrfResponse = await res.json()
|
||||
cachedToken = data.token
|
||||
return cachedToken
|
||||
export function getCsrfToken(): Promise<string> {
|
||||
if (cachedToken) return Promise.resolve(cachedToken)
|
||||
if (inflight) return inflight
|
||||
|
||||
inflight = fetch(`${API_URL}/csrf/token`, { credentials: 'include' })
|
||||
.then((res) => res.json() as Promise<CsrfResponse>)
|
||||
.then((data) => {
|
||||
cachedToken = data.token
|
||||
inflight = null
|
||||
return cachedToken
|
||||
})
|
||||
.catch((err) => {
|
||||
inflight = null
|
||||
throw err
|
||||
})
|
||||
|
||||
return inflight
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
20
src/shared/assets/coins/bnb.svg
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 26.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 2496 2496" style="enable-background:new 0 0 2496 2496;" xml:space="preserve">
|
||||
<g>
|
||||
<path style="fill-rule:evenodd;clip-rule:evenodd;fill:#F0B90B;" d="M1248,0c689.3,0,1248,558.7,1248,1248s-558.7,1248-1248,1248
|
||||
S0,1937.3,0,1248S558.7,0,1248,0L1248,0z"/>
|
||||
<path style="fill:#FFFFFF;" d="M685.9,1248l0.9,330l280.4,165v193.2l-444.5-260.7v-524L685.9,1248L685.9,1248z M685.9,918v192.3
|
||||
l-163.3-96.6V821.4l163.3-96.6l164.1,96.6L685.9,918L685.9,918z M1084.3,821.4l163.3-96.6l164.1,96.6L1247.6,918L1084.3,821.4
|
||||
L1084.3,821.4z"/>
|
||||
<path style="fill:#FFFFFF;" d="M803.9,1509.6v-193.2l163.3,96.6v192.3L803.9,1509.6L803.9,1509.6z M1084.3,1812.2l163.3,96.6
|
||||
l164.1-96.6v192.3l-164.1,96.6l-163.3-96.6V1812.2L1084.3,1812.2z M1645.9,821.4l163.3-96.6l164.1,96.6v192.3l-164.1,96.6V918
|
||||
L1645.9,821.4L1645.9,821.4L1645.9,821.4z M1809.2,1578l0.9-330l163.3-96.6v524l-444.5,260.7v-193.2L1809.2,1578L1809.2,1578
|
||||
L1809.2,1578z"/>
|
||||
<polygon style="fill:#FFFFFF;" points="1692.1,1509.6 1528.8,1605.3 1528.8,1413 1692.1,1316.4 1692.1,1509.6 "/>
|
||||
<path style="fill:#FFFFFF;" d="M1692.1,986.4l0.9,193.2l-281.2,165v330.8l-163.3,95.7l-163.3-95.7v-330.8l-281.2-165V986.4
|
||||
L968,889.8l279.5,165.8l281.2-165.8l164.1,96.6H1692.1L1692.1,986.4z M803.9,656.5l443.7-261.6l444.5,261.6l-163.3,96.6
|
||||
l-281.2-165.8L967.2,753.1L803.9,656.5L803.9,656.5z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
43
src/shared/assets/coins/bonk.svg
Normal file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="CIRCLE_OUTLINE_BLACK" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
x="0px" y="0px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FDDE00;}
|
||||
.st1{fill:#3C2D0C;}
|
||||
.st2{fill:#E78C19;}
|
||||
.st3{fill:#EFB99D;}
|
||||
.st4{fill:#FBFBFB;}
|
||||
.st5{fill:#E72D36;}
|
||||
</style>
|
||||
<circle class="st0" cx="256" cy="256" r="256"/>
|
||||
<path class="st1" d="M131.4,181c0,0-13.2,16.7-8.8,92.2c0,0-28.9,74-19.1,111.8c9.8,37.7,48,35.8,48,35.8s61.3,53.9,108.3,6.4
|
||||
c47.1-47.6-39.2-231.9-39.2-231.9l-10.8-63.7c0,0-11.8,9.3-10.3,70.6s-24,18.1-24,18.1L131.4,181z"/>
|
||||
<path class="st2" d="M136.3,247.7l-16.2,45.6l-5.9,31.9c0,0-20.1,71.1,62.8,70.1c0,0,64.7,57.9,106.9,19.1l11.3-12.3l19.6-30.4
|
||||
l32.4-96.6c0,0-10.3-66.2-75.5-93.6c0,0-5.4-2.9-15.2,1c0,0-27-60.8-44.1-53.4c0,0-14.7,7.8-2,67.7l-23,10.3
|
||||
c0,0-25.2-47.8-48.5-38.2C117.2,177.6,136.3,247.7,136.3,247.7z"/>
|
||||
<path class="st3" d="M220.1,158.9l2,26.5c0,0,1.5,15.2,17.6,9.8c4.6-1.5,8.6-4.7,8.2-9.9c-0.3-3.9-3-7.5-4.8-10.8
|
||||
c-3.3-6.3-6-13-9.9-19.1c-2-3.1-6.1-10-10.6-9.2C222.6,146.2,219.2,145.2,220.1,158.9z"/>
|
||||
<path class="st3" d="M149.3,188.7c0.3-0.2,6.9-8.5,20.6,11.7c13.7,20.2,12.3,17.6,12.3,17.6s7.4,8.1-7.7,18.5
|
||||
c-3.3,2.4-6.9,4.4-10.7,6c-2.4,0.9-4.9,1.4-7.4,1.4c-2.9-0.1-4.2-1.6-4.9-4.3c-2.3-8.8-4.9-17.6-6.2-26.6
|
||||
C144.3,206,142.3,193.2,149.3,188.7z"/>
|
||||
<path class="st4" d="M308.5,229.4c0,4.8-7,8.7-15.7,8.7c-8.7,0-15.7-3.9-15.7-8.7s7.2-11.9,15.8-11.9S308.5,224.6,308.5,229.4z"/>
|
||||
<ellipse transform="matrix(0.4925 -0.8703 0.8703 0.4925 -121.8385 314.1253)" class="st4" cx="208.4" cy="261.5" rx="17.1" ry="11"/>
|
||||
<path class="st4" d="M240.2,306.7l42.9-22.3c0,0,53.4-27.9,66.4-6.4s0,54.4,0,54.4s-5.6,38.5-44.9,41.9l-21.1,2.9l-27,0.7
|
||||
c0,0-43.4,2.5-75.3-21.1l-10.1-12.3c0,0-20.3-27.9-6.1-48s62.8,11,62.8,11S234.6,311.2,240.2,306.7z"/>
|
||||
<circle class="st5" cx="337.5" cy="185.2" r="10.3"/>
|
||||
<circle class="st5" cx="314.7" cy="159.9" r="10.3"/>
|
||||
<circle class="st5" cx="278.2" cy="151.8" r="10.3"/>
|
||||
<path class="st5" d="M286.3,78.8c2.2,30.4-2.2,57.9-9.6,57.9s-17.2-29.7-17.2-57.9c0-11.6,6.1-13.5,13.5-13.5
|
||||
C280.5,65.3,285.5,67.2,286.3,78.8z"/>
|
||||
<path class="st5" d="M354.5,97.7c-11.2,28.3-27.1,51.2-33.7,48c-6.6-3.2-2.6-34.2,9.7-59.6c5.1-10.5,11.4-9.5,18-6.3
|
||||
C355.1,83.1,358.8,86.9,354.5,97.7z"/>
|
||||
<path class="st5" d="M403.3,152.9c-24.7,17.9-50.3,28.7-54.2,22.4c-3.9-6.3,16.2-30.2,40.1-45.1c9.9-6.1,14.7-1.9,18.6,4.3
|
||||
C411.6,140.8,412.7,146,403.3,152.9z"/>
|
||||
<path d="M238.2,276.8c-0.9,0.1-1.8,0.2-2.7,0.3c0.1,0.3,0.2,0.6,0.2,1c0,3.1-4.9,5.7-11,5.9c0,0.3-0.1,0.5,0,0.8
|
||||
c0.3,3.8,6.9,6.4,14.6,5.8c7.8-0.6,13.9-4.2,13.6-8C252.5,278.8,246,276.2,238.2,276.8z"/>
|
||||
<path d="M292.4,263.4c-0.9,0.1-1.8,0.2-2.7,0.3c0.1,0.3,0.2,0.6,0.2,1c0,3.1-4.9,5.7-11,5.9c0,0.3-0.1,0.5,0,0.8
|
||||
c0.3,3.8,6.9,6.4,14.6,5.8c7.8-0.6,13.9-4.2,13.6-8C306.8,265.4,300.2,262.8,292.4,263.4z"/>
|
||||
<path d="M310.2,279.3c0,0-21.5,6.3-20.2,18.4s27.2,18.4,27.2,18.4s7,2.8,11-3.9c4-6.6,9.9-15.6,8.3-28.5
|
||||
C336.5,283.7,336,274.1,310.2,279.3z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 518 B After Width: | Height: | Size: 518 B |
1
src/shared/assets/coins/busd.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 336.41 337.42"><defs><style>.cls-1{fill:#f0b90b;stroke:#f0b90b;}</style></defs><title>Asset 1</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M168.2.71l41.5,42.5L105.2,147.71l-41.5-41.5Z"/><path class="cls-1" d="M231.2,63.71l41.5,42.5L105.2,273.71l-41.5-41.5Z"/><path class="cls-1" d="M42.2,126.71l41.5,42.5-41.5,41.5L.7,169.21Z"/><path class="cls-1" d="M294.2,126.71l41.5,42.5L168.2,336.71l-41.5-41.5Z"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 528 B |
13
src/shared/assets/coins/dai.svg
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Creator: CorelDRAW 2019 (64-Bit) -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="100%" height="100%" version="1.1" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd"
|
||||
viewBox="0 0 444.44 444.44"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:xodm="http://www.corel.com/coreldraw/odm/2003">
|
||||
<g id="Layer_x0020_1">
|
||||
<metadata id="CorelCorpID_0Corel-Layer"/>
|
||||
<path fill="#F5AC37" fill-rule="nonzero" d="M222.22 0c122.74,0 222.22,99.5 222.22,222.22 0,122.74 -99.48,222.22 -222.22,222.22 -122.72,0 -222.22,-99.49 -222.22,-222.22 0,-122.72 99.5,-222.22 222.22,-222.22z"/>
|
||||
<path fill="#FEFEFD" fill-rule="nonzero" d="M230.41 237.91l84.44 0c1.8,0 2.65,0 2.78,-2.36 0.69,-8.59 0.69,-17.23 0,-25.83 0,-1.67 -0.83,-2.36 -2.64,-2.36l-168.05 0c-2.08,0 -2.64,0.69 -2.64,2.64l0 24.72c0,3.19 0,3.19 3.33,3.19l82.78 0zm77.79 -59.44c0.24,-0.63 0.24,-1.32 0,-1.94 -1.41,-3.07 -3.08,-6 -5.02,-8.75 -2.92,-4.7 -6.36,-9.03 -10.28,-12.92 -1.85,-2.35 -3.99,-4.46 -6.39,-6.25 -12.02,-10.23 -26.31,-17.47 -41.67,-21.11 -7.75,-1.74 -15.67,-2.57 -23.61,-2.5l-74.58 0c-2.08,0 -2.36,0.83 -2.36,2.64l0 49.3c0,2.08 0,2.64 2.64,2.64l160.27 0c0,0 1.39,-0.28 1.67,-1.11l-0.68 0zm0 88.33c-2.36,-0.26 -4.74,-0.26 -7.1,0l-154.02 0c-2.08,0 -2.78,0 -2.78,2.78l0 48.2c0,2.22 0,2.78 2.78,2.78l71.11 0c3.4,0.26 6.8,0.02 10.13,-0.69 10.32,-0.74 20.47,-2.98 30.15,-6.67 3.52,-1.22 6.92,-2.81 10.13,-4.72l0.97 0c16.67,-8.67 30.21,-22.29 38.75,-39.01 0,0 0.97,-2.1 -0.12,-2.65zm-191.81 78.75l0 -0.83 0 -32.36 0 -10.97 0 -32.64c0,-1.81 0,-2.08 -2.22,-2.08l-30.14 0c-1.67,0 -2.36,0 -2.36,-2.22l0 -26.39 32.22 0c1.8,0 2.5,0 2.5,-2.36l0 -26.11c0,-1.67 0,-2.08 -2.22,-2.08l-30.14 0c-1.67,0 -2.36,0 -2.36,-2.22l0 -24.44c0,-1.53 0,-1.94 2.22,-1.94l29.86 0c2.08,0 2.64,0 2.64,-2.64l0 -74.86c0,-2.22 0,-2.78 2.78,-2.78l104.16 0c7.56,0.3 15.07,1.13 22.5,2.5 15.31,2.83 30.02,8.3 43.47,16.11 8.92,5.25 17.13,11.59 24.44,18.89 5.5,5.71 10.46,11.89 14.86,18.47 4.37,6.67 8,13.8 10.85,21.25 0.35,1.94 2.21,3.25 4.15,2.92l24.86 0c3.19,0 3.19,0 3.33,3.06l0 22.78c0,2.22 -0.83,2.78 -3.06,2.78l-19.17 0c-1.94,0 -2.5,0 -2.36,2.5 0.76,8.46 0.76,16.95 0,25.41 0,2.36 0,2.64 2.65,2.64l21.93 0c0.97,1.25 0,2.5 0,3.76 0.14,1.61 0.14,3.24 0,4.85l0 16.81c0,2.36 -0.69,3.06 -2.78,3.06l-26.25 0c-1.83,-0.35 -3.61,0.82 -4.03,2.64 -6.25,16.25 -16.25,30.82 -29.17,42.5 -4.72,4.25 -9.68,8.25 -14.86,11.94 -5.56,3.2 -10.97,6.53 -16.67,9.17 -10.49,4.72 -21.49,8.2 -32.78,10.41 -10.72,1.92 -21.59,2.79 -32.5,2.64l-96.39 0 0 -0.14z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
1
src/shared/assets/coins/doge.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2000 2000" width="2500" height="2500"><g fill="#c2a633"><path d="M1024 659H881.12v281.69h224.79v117.94H881.12v281.67H1031c38.51 0 316.16 4.35 315.73-327.72S1077.44 659 1024 659z"/><path d="M1000 0C447.71 0 0 447.71 0 1000s447.71 1000 1000 1000 1000-447.71 1000-1000S1552.29 0 1000 0zm39.29 1540.1H677.14v-481.46H549.48V940.7h127.65V459.21h310.82c73.53 0 560.56-15.27 560.56 549.48 0 574.09-509.21 531.41-509.21 531.41z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 484 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
49
src/shared/assets/coins/index.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import btc from './btc.svg'
|
||||
import eth from './eth.svg'
|
||||
import sol from './sol.svg'
|
||||
import trx from './trx.svg'
|
||||
import bnb from './bnb.svg'
|
||||
import arb from './arb.svg'
|
||||
import bonk from './bonk.svg'
|
||||
import busd from './busd.svg'
|
||||
import dai from './dai.svg'
|
||||
import doge from './doge.svg'
|
||||
import jup from './jup.svg'
|
||||
import link from './link.svg'
|
||||
import orca from './orca.svg'
|
||||
import popcat from './popcat.svg'
|
||||
import pyth from './pyth.svg'
|
||||
import ray from './ray.svg'
|
||||
import uni from './uni.svg'
|
||||
import usdc from './usdc.svg'
|
||||
import usdt from './usdt.svg'
|
||||
import w from './w.svg'
|
||||
import wif from './wif.svg'
|
||||
|
||||
export const COIN_ICONS: Record<string, string> = {
|
||||
BTC: btc,
|
||||
ETH: eth,
|
||||
SOL: sol,
|
||||
TRX: trx,
|
||||
BNB: bnb,
|
||||
ARB: arb,
|
||||
BONK: bonk,
|
||||
BUSD: busd,
|
||||
DAI: dai,
|
||||
DOGE: doge,
|
||||
JUP: jup,
|
||||
LINK: link,
|
||||
ORCA: orca,
|
||||
POPCAT: popcat,
|
||||
PYTH: pyth,
|
||||
RAY: ray,
|
||||
UNI: uni,
|
||||
USDC: usdc,
|
||||
USDT: usdt,
|
||||
W: w,
|
||||
WIF: wif,
|
||||
}
|
||||
|
||||
export function getCoinIcon(ticker: string): string | undefined {
|
||||
return COIN_ICONS[ticker.toUpperCase()]
|
||||
}
|
||||
52
src/shared/assets/coins/jup.svg
Normal file
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="katman_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 800 800" style="enable-background:new 0 0 800 800;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#141726;}
|
||||
.st1{fill:url(#SVGID_1_);}
|
||||
.st2{fill:url(#SVGID_2_);}
|
||||
.st3{fill:url(#SVGID_3_);}
|
||||
.st4{fill:url(#SVGID_4_);}
|
||||
.st5{fill:url(#SVGID_5_);}
|
||||
.st6{fill:url(#SVGID_6_);}
|
||||
</style>
|
||||
<circle class="st0" cx="400" cy="400" r="400"/>
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="574.9257" y1="665.8727" x2="248.5257" y2="142.3127" gradientTransform="matrix(1 0 0 -1 0 800)">
|
||||
<stop offset="0.16" style="stop-color:#C6F462"/>
|
||||
<stop offset="0.89" style="stop-color:#33D9FF"/>
|
||||
</linearGradient>
|
||||
<path class="st1" d="M536,568.9c-66.8-108.5-166.4-170-289.4-195.6c-43.5-9-87.2-8.9-129.4,7.7c-28.9,11.4-33.3,23.4-19.7,53.7
|
||||
c92.4-21.9,178.4-1.5,258.9,45c81.1,46.9,141.6,112.2,169.1,205c38.6-11.8,43.6-18.3,34.3-54.2C554.3,609.4,547.4,587.4,536,568.9
|
||||
L536,568.9z"/>
|
||||
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="572.5896" y1="667.3303" x2="246.1996" y2="143.7703" gradientTransform="matrix(1 0 0 -1 0 800)">
|
||||
<stop offset="0.16" style="stop-color:#C6F462"/>
|
||||
<stop offset="0.89" style="stop-color:#33D9FF"/>
|
||||
</linearGradient>
|
||||
<path class="st2" d="M609.1,480.6c-85.8-125-207.3-194.9-355.8-218.3c-39.3-6.2-79.4-4.5-116.2,14.3c-17.6,9-33.2,20.5-37.4,44.9
|
||||
c115.8-31.9,219.7-3.7,317.5,53c98.3,57,175.1,133.5,205,251.1c20.8-18.4,24.5-41,19.1-62C633.9,534.8,625.5,504.5,609.1,480.6
|
||||
L609.1,480.6z"/>
|
||||
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="577.0148" y1="664.5671" x2="250.6247" y2="141.0071" gradientTransform="matrix(1 0 0 -1 0 800)">
|
||||
<stop offset="0.16" style="stop-color:#C6F462"/>
|
||||
<stop offset="0.89" style="stop-color:#33D9FF"/>
|
||||
</linearGradient>
|
||||
<path class="st3" d="M105,488.6c7.3,16.2,12.1,34.5,23,47.6c5.5,6.7,22.2,4.1,33.8,5.7c1.8,0.2,3.6,0.5,5.4,0.7
|
||||
c102.9,15.3,184.1,65.1,242.1,152c3.4,5.1,8.9,12.7,13.4,12.7c17.4-0.1,34.9-2.8,52.5-4.5C449,557.5,232.8,438.3,105,488.6
|
||||
L105,488.6z"/>
|
||||
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="569.0272" y1="669.5518" x2="242.6272" y2="145.9917" gradientTransform="matrix(1 0 0 -1 0 800)">
|
||||
<stop offset="0.16" style="stop-color:#C6F462"/>
|
||||
<stop offset="0.89" style="stop-color:#33D9FF"/>
|
||||
</linearGradient>
|
||||
<path class="st4" d="M656.6,366.7C599.9,287.4,521.7,234.6,432.9,197c-61.5-26.1-125.2-41.8-192.8-33.7
|
||||
c-23.4,2.8-45.3,9.5-63.4,24.7c230.9,5.8,404.6,105.8,524,303.3c0.2-13.1,2.2-27.7-2.6-39.5C686.1,422.5,674.7,392,656.6,366.7z"/>
|
||||
<linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="571.6973" y1="667.8917" x2="245.2973" y2="144.3317" gradientTransform="matrix(1 0 0 -1 0 800)">
|
||||
<stop offset="0.16" style="stop-color:#C6F462"/>
|
||||
<stop offset="0.89" style="stop-color:#33D9FF"/>
|
||||
</linearGradient>
|
||||
<path class="st5" d="M709.8,325.3c-47-178.9-238-265-379.2-221.4C482.7,133.9,607.5,206.4,709.8,325.3z"/>
|
||||
<linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="579.0382" y1="663.3111" x2="252.6482" y2="139.7511" gradientTransform="matrix(1 0 0 -1 0 800)">
|
||||
<stop offset="0.16" style="stop-color:#C6F462"/>
|
||||
<stop offset="0.89" style="stop-color:#33D9FF"/>
|
||||
</linearGradient>
|
||||
<path class="st6" d="M155.4,583.9c54.6,69.3,124,109.7,213,122.8C334.4,643.2,214.6,574.5,155.4,583.9L155.4,583.9z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
1
src/shared/assets/coins/link.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 37.8 43.6"><defs><style>.cls-1{fill:#2a5ada;}</style></defs><title>Asset 1</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M18.9,0l-4,2.3L4,8.6,0,10.9V32.7L4,35l11,6.3,4,2.3,4-2.3L33.8,35l4-2.3V10.9l-4-2.3L22.9,2.3ZM8,28.1V15.5L18.9,9.2l10.9,6.3V28.1L18.9,34.4Z"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 387 B |
40
src/shared/assets/coins/orca.svg
Normal file
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="CIRCLE_OUTLINE_BLACK" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
x="0px" y="0px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFD15C;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
</style>
|
||||
<path class="st0" d="M512,256c0,141.4-114.6,256-256,256S0,397.4,0,256S114.6,0,256,0S512,114.6,512,256z"/>
|
||||
<path class="st1" d="M193.5,152.3c-10.4,1.5-4.6,10.4-4.6,10.4l-0.3,2.5c14.9,25.7,39.5,52.7,64.6,70.7c18.5,13.2,32.3,32.7,39,54.9
|
||||
c6.6,21.9,5.4,44.6-3.2,62.2c-10.4,21.2-30.1,33.5-55.3,34.7c-25.1,1.2-54.9-8.7-88.6-29.4c-0.3,0.8-0.7,1.6-1.1,2.3
|
||||
c29.2,30.1,63,48.1,98.1,52.2c35.5,4.2,71.9-5.8,102.7-27.9c-0.8,0-1.4-0.2-1.9-0.5C446.5,298.8,276.9,140.4,193.5,152.3
|
||||
L193.5,152.3z"/>
|
||||
<path d="M462.6,267.4c-0.1-1.9-0.2-4.2-0.6-6.9c-1.3-10.5-5.5-26.6-17.7-43.7c-0.6-0.8-1.2-1.6-1.8-2.4c-0.1-0.2-0.3-0.4-0.4-0.6
|
||||
c-0.5-0.7-1.1-1.4-1.6-2.1c0,0-0.1-0.1-0.1-0.1c-0.6-0.7-1.2-1.5-1.8-2.2c-0.1-0.2-0.3-0.3-0.4-0.5c-0.5-0.6-1.1-1.3-1.6-1.9
|
||||
c-0.1-0.1-0.1-0.1-0.2-0.2c-0.6-0.7-1.2-1.3-1.8-2c-0.1-0.1-0.3-0.3-0.4-0.4c-0.5-0.6-1-1.1-1.5-1.6c-0.1-0.1-0.2-0.2-0.2-0.3
|
||||
c-0.6-0.6-1.2-1.2-1.7-1.7c-0.1-0.1-0.2-0.2-0.4-0.4c-0.5-0.5-1-1-1.5-1.4c-0.1-0.1-0.2-0.2-0.3-0.3c-0.6-0.5-1.1-1-1.7-1.5
|
||||
c-0.1-0.1-0.2-0.2-0.3-0.3c-0.5-0.4-1-0.8-1.4-1.2c-0.1-0.1-0.2-0.2-0.3-0.3c-0.5-0.5-1.1-0.9-1.6-1.3c-0.1-0.1-0.2-0.2-0.3-0.3
|
||||
c-0.4-0.4-0.9-0.7-1.3-1.1c-0.1-0.1-0.3-0.2-0.4-0.3c-0.5-0.4-1-0.8-1.5-1.1c-0.1-0.1-0.2-0.2-0.3-0.2c-0.4-0.3-0.8-0.6-1.2-0.9
|
||||
c-0.1-0.1-0.3-0.2-0.4-0.3c-0.5-0.3-0.9-0.6-1.4-1c-0.1-0.1-0.2-0.2-0.4-0.2c-0.4-0.2-0.7-0.5-1-0.7c-0.2-0.1-0.3-0.2-0.5-0.3
|
||||
c-0.4-0.2-0.8-0.5-1.1-0.7c-0.2-0.1-0.4-0.2-0.5-0.3c-0.3-0.2-0.5-0.3-0.8-0.5c-0.2-0.1-0.4-0.2-0.6-0.3c-0.2-0.1-0.5-0.3-0.7-0.4
|
||||
c-0.3-0.2-0.6-0.4-1-0.5c-0.2-0.1-0.3-0.2-0.5-0.3c-0.2-0.1-0.4-0.2-0.7-0.3c-0.1-0.1-0.2-0.1-0.3-0.2c-0.4-0.2-0.9-0.5-1.3-0.7
|
||||
c0,0-0.1,0-0.1,0c-2.7-4.8-5.7-9.6-8.9-14.4c-37.3-54.6-78.2-81.1-124.9-81.2h-0.2c-19.8,0-37.9,4.5-53,8.9c-4.3,1.3-8.8,2.6-13.1,4
|
||||
c-11,3.4-21.4,6.7-30.3,8.1c-5.6,0.9-10,3-13.1,6.2c-2.8,3-4.5,7.1-5,12.1c-1,9.7,2.5,22.8,10.1,37.8c15.3,30.4,44.6,63.9,74.6,85.4
|
||||
c3.5,2.5,6.7,5.2,9.7,8.2c-0.6,0.1-1.2,0.2-1.9,0.3c-23.5,3.4-35.8,28.6-40.7,42.3c-1.3,3.6,2.2,6.8,5.8,5.5
|
||||
c14.6-4.9,44.3-12.9,61-2.6c0.1,0,0.3-0.1,0.4-0.1c3,15,1.6,29.8-4.1,41.3c-7.5,15.1-21.8,24-40.3,24.9
|
||||
c-21.8,1.1-48.6-8.2-79.7-27.6l-0.5-0.3c-0.3-0.1-0.6-0.3-0.8-0.5c24.2-27.5,9.9-69.9,4.3-83.8c-0.7-1.8-3.3-1.8-4.3-0.1
|
||||
c-13,22.1-39.8,20.4-39.3,46.7c0,0,0,0,0-0.1c-20.4-16.6-35.6,5.5-61,2.2c-2-0.3-3.5,1.7-2.5,3.4c7.7,13.3,33.5,52.3,71.6,47.9
|
||||
l-1.8,1.3l6.8,7.5c33.3,36.7,72.9,58.7,114.6,63.6c6,0.7,12,1.1,18,1.1c34.9,0,69.6-11.7,99.4-33.9c34.2-25.4,57.9-61.3,66.8-101
|
||||
c3-13.2,4.2-26.4,3.7-39.5c7.5-3,15.7,0.3,22.2,4.7c0.1,0,0.1,0.1,0.2,0.1c0.3,0.2,0.5,0.4,0.8,0.5c0.1,0,0.1,0.1,0.2,0.1
|
||||
c0.8,0.5,1.5,1.1,2.2,1.6c0.1,0.1,0.3,0.2,0.4,0.3c0.2,0.1,0.3,0.3,0.5,0.4c0.2,0.1,0.3,0.3,0.5,0.4c0.1,0.1,0.3,0.2,0.4,0.4
|
||||
c0.3,0.2,0.6,0.5,0.8,0.7c0.1,0.1,0.2,0.2,0.3,0.2c0.2,0.2,0.4,0.4,0.6,0.5c0.1,0.1,0.2,0.2,0.3,0.3c0.2,0.2,0.4,0.3,0.6,0.5
|
||||
c0.1,0.1,0.2,0.1,0.2,0.2c0.2,0.2,0.5,0.5,0.7,0.7c0.1,0.1,0.2,0.1,0.2,0.2c0.5,0.4,1.1,0.5,1.6,0.5
|
||||
C461.7,269.5,462.7,268.6,462.6,267.4z M342.9,384.5c0.5,0.3,1.1,0.4,1.9,0.5c-30.8,22.2-67.2,32.1-102.7,27.9
|
||||
c-35.2-4.1-68.9-22.1-98.1-52.2c0.4-0.8,0.8-1.5,1.1-2.3c33.7,20.7,63.5,30.6,88.6,29.4c25.2-1.2,44.9-13.6,55.3-34.7
|
||||
c8.7-17.6,9.8-40.2,3.2-62.2c-6.7-22.2-20.5-41.7-39-54.9c-25.2-18-49.7-45-64.6-70.7l0.3-2.5c0,0-5.8-8.9,4.6-10.4
|
||||
C276.9,140.4,446.5,298.8,342.9,384.5z"/>
|
||||
<path class="st1" d="M280.3,146.5c-2.9-4.1-8.7-15.1,9.3-15.1c18,0,45.4,20.3,51,27.8c-1.7,4.6-13.3,5.6-19.1,5.2
|
||||
s-15.6-1.4-23.2-5.2C290.8,155.6,283.2,150.6,280.3,146.5z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
97
src/shared/assets/coins/popcat.svg
Normal file
@@ -0,0 +1,97 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Слой_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 994.3 1000.5" style="enable-background:new 0 0 994.3 1000.5;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#F7F5F8;}
|
||||
.st1{fill:#DCC4D4;}
|
||||
.st2{fill:#EFAD93;}
|
||||
.st3{fill:#D68C72;}
|
||||
.st4{fill:#964C49;}
|
||||
.st5{fill:#3A0101;}
|
||||
.st6{fill:#894544;}
|
||||
.st7{fill:#E3746D;}
|
||||
.st8{fill:#FFFFFF;}
|
||||
.st9{fill:#623538;}
|
||||
.st10{fill:#C95755;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M27.4,444.9l471.8-254l124.2-2.5c0,0,129.7,15.8,232.7,116.6c0.1,0.1,0.2,0.1,0.2,0.2
|
||||
c28.2,27.7,54.4,62.5,75.1,106.3c1.4,2.9,2.8,5.9,4.1,8.9c0,0,151.2,244-41.3,456.4c-192.5,212.3-651.4,110.6-789.3-112.5
|
||||
c-1-1.5-2-3.1-3-4.7c-21.2-34.1-37-67.9-48.6-99.2c-14.7-39.6-22.9-75.1-27.4-101.6C20.8,528.5,27.5,444.9,27.4,444.9z"/>
|
||||
<path class="st1" d="M218.8,420.3c-31.3,88.9,7.8,189.9,85.9,99.6c111.2-153,321.4-183.3,405.1-172.5
|
||||
c102.5,19.8,182.5,123.4,207.9,125.4c25.4,2,13.7-61.3,13.7-61.3c1.4,2.9,2.8,5.9,4.1,8.9c0,0,151.2,244-41.3,456.4
|
||||
C701.7,1089.1,242.8,987.4,105,764.2c-1-1.5-2-3.1-3-4.7c-21.2-34.1-37-67.9-48.6-99.2C106.8,572.1,243.9,348.5,218.8,420.3z"/>
|
||||
<path class="st2" d="M680.7,183.7c49.4-48.5,129.9-117.2,190.6-152.4c29.2-17,53.8-26.2,68.1-21.7c2.8,0.8,5.1,2.2,7.1,4.1
|
||||
c23.7,23.2,14.4,169.4,3.6,279.9c-7,70.6-14.6,126.7-14.6,126.7c-15.5-33.2-32-65.7-81.8-118c-20.7-20.3-41.8-37.4-63.2-51.7
|
||||
c-36-24-72.5-40.1-108.3-50.3c-4.8-1.4-9.6-2.6-14.4-3.8C671.8,192.5,676.1,188.2,680.7,183.7z"/>
|
||||
<path class="st3" d="M871.3,31.4c-10.1,73.5-12.2,200.9,78.8,262.3c-7,70.6-14.6,126.7-14.6,126.7c-15.5-33.2-32-65.7-81.8-118
|
||||
c-20.7-20.3-41.8-37.4-63.2-51.7c-36-24-72.5-40.1-108.3-50.3l-1.6-16.6C730.1,135.2,810.6,66.6,871.3,31.4z"/>
|
||||
<path class="st4" d="M51.1,14.9c49.7-15.8,213.2,136.5,315.7,216.5c0,0,141.2-74,301.1-34.9c7.5,1.8,15.1,3.9,22.8,6.3
|
||||
c-46.6-1.7-115.4-3.1-144.9,2.2C499,213.5,329,264,254.7,383.2c-74.3,119.1-71.3,258.8-69.5,320.2c1.4,48.9-34.4,79.5-88.3,48
|
||||
c-44.5-74.3-63.2-146.8-71-192.6c-1.1-10.1-2.2-20.5-3.5-31c-0.8-6-1.4-12-2.1-17.9C-12.3,223.4,2.5,30.3,51.1,14.9z"/>
|
||||
<path class="st4" d="M939.4,9.6c-41.2,32.2-128.1,113.5-148.9,241c-40.8-27.2-82.3-44.2-122.7-54.1C736,126.7,892.6-5,939.4,9.6z"
|
||||
/>
|
||||
<path class="st5" d="M667.3,196.7c18-21.7,38.7-41.3,59-60.8c30.9-28.7,63.1-56.1,97.5-80.5c23.1-16.3,47-31.5,73.2-42.8
|
||||
c13.8-5.3,28.2-11.2,43.7-6.9c3.2,0.8,4.1,5.5,1.3,7.4c-48,35.5-89.8,81.2-115,136l-7.3,16.4l-6.3,16.8c-8.3,23.1-13.5,47.2-19,71
|
||||
c-1.4,2.2-4.4,2.7-6.5,1.3c-9.3-6.2-18.7-12.1-28.3-17.7C730.6,220,699.1,207.3,667.3,196.7L667.3,196.7z M668.3,196.4
|
||||
c33.5,5.2,65.5,17.4,95.4,33c10,5.2,19.8,11.1,29.3,17.3l-7.2,3.2c11.1-73.1,51.8-137.4,99-192.5c15.9-18.4,33.2-35.6,52-51.1
|
||||
l1.3,7.4c-34.7-7-111.7,54.4-141.3,75.9C755.4,121.4,714.9,155,676,189.9C673.8,191.8,670.7,194.7,668.3,196.4L668.3,196.4z"/>
|
||||
<path class="st5" d="M667,196.8c9.8-12.7,21.5-23.8,32.5-35.3c56.3-55.7,115.3-109,185.4-147.2C903.6,5.5,934-9.3,952.6,8
|
||||
c8.6,9.3,11,23.4,12.9,34.8c7.6,63.5,0.5,127.1-5.9,190.2c-7.1,62.7-14.3,125.2-20.2,188c-0.1,1.2-0.9,2.5-2.1,3.1
|
||||
c-1.9,1-4.3,0.2-5.3-1.7c-21.3-43.7-51.6-81.8-85.2-116.5c-29-28.5-61.5-53.6-97.1-73.3C723.7,217.7,695.1,206.6,667,196.8
|
||||
L667,196.8z M668.6,196.3c102.9,16.6,200.3,90.3,251,181.1c7.4,13.4,13.7,27.4,19.6,41.3l-7.6,1.2c15.8-93,21.6-187.6,22.5-281.8
|
||||
c0.1-30.9,0.3-62.6-4.6-92.8c-1.5-8.2-3.1-16-6.7-22.5c-2.2-3.9-5.1-5.3-10-5.6c-13.2-0.3-28,6.5-40.3,12.5
|
||||
c-13.6,6.6-26.9,14.5-39.9,22.9c-62,40.5-119.8,87.7-175.4,136.6C674.9,191.3,671.1,194.7,668.6,196.3L668.6,196.3z"/>
|
||||
<path class="st5" d="M368.1,222.4c0,0-27.7,15.2-48.3,39.2c32.8-26.4,66.9-39.2,66.9-39.2H368.1z"/>
|
||||
<path class="st5" d="M51.4,15.6C-13.1,43.9,7.5,316.1,13.6,383c3.6,42.3,8.6,84.5,13.7,126.7c0.9,16.6,3.4,33.8,6.5,50.3
|
||||
c15.8,83.2,49,164.3,101,231.4c162.6,201.4,596.3,285.2,772.8,64.1c93.2-112.3,103.3-255.1,47.5-387.3c-6.8-15.7-14.3-31.3-23-45.7
|
||||
c-9.8-21.3-21.3-41.7-34.7-60.8C858,307,809.5,265.5,746.8,233.1c-117.3-59-260-50.3-377.9,2.6c-1.6,0.8-3.7,0.7-5.2-0.5
|
||||
c-50.3-40.8-98.5-83.9-148.5-125C183.7,85.2,89.5,5.3,51.4,15.6L51.4,15.6z M50.9,14.1c38.2-10.9,133.1,68.2,166.8,93
|
||||
c51.1,39.6,100.6,81.4,152,120.5l-5.2-0.5c10.1-5.3,20.2-9.7,30.6-13.9c113.6-45.9,247.7-50.1,357.9,7.6
|
||||
c38.5,19.3,80,51.8,106.2,79.6c28.3,26.3,63.5,77.6,79.6,117.9c37.5,68.6,57.7,147.2,55.3,225.5c-2,78.7-32.2,155.1-80.9,216.3
|
||||
c-144.1,184.7-435.8,164.2-626.6,69.8c-70.3-34.9-136.1-83.3-181-148.9c-52.9-79.3-87.3-177.1-92-270.3
|
||||
c-12-126.6-19.9-254.6-7-381.4C10.8,98.9,18.7,25.8,50.9,14.1L50.9,14.1z"/>
|
||||
<path class="st2" d="M56.5,68.9c10.8,4.5,48,42.6,88.9,92c33.1,40,68.6,87.5,94.4,130.5C256.3,318.9,134.9,522,113,560.3
|
||||
c-20.1,35.1-64.5-92-76.1-226c-1.1-12-1.8-23.9-2.3-35.9C28.8,152.3,36.9,60.8,56.5,68.9z"/>
|
||||
<path class="st6" d="M327.6,643.2c7.1-85.1,52.7-166.4,117.6-206.6c75-42,139.1-61.4,202.6-61.1l0,0c15.2,0.1,30.5,1.3,45.8,3.6
|
||||
c15,2.3,30.1,5.6,45.5,9.9c66,18.5,208.6,88.2,186.5,282.8c-0.7,5.8-1.5,11.5-2.5,17.2C891,873.9,688.2,995.2,467.4,887.4
|
||||
C358,834,319.8,736.3,327.6,643.2z"/>
|
||||
<path class="st7" d="M636.5,336.4c9.7,2.9,32.2,11.3,56.1-1c23.9-12.3,33.2,15.1,20.5,22.1c-14.4,7.9-18.4,17-19.6,21.7
|
||||
c-15.3-2.3-30.5-3.5-45.8-3.6l0,0c-4.1-7.4-25.4-8.4-33.2-21.1C606.7,341.7,626.8,333.4,636.5,336.4z"/>
|
||||
<path class="st8" d="M386.7,314c6.2-5.8,12.6-10.9,19.1-15.3c58-40,121.3-28.3,130.8-3.7c10.6,27.3-35.8,36.7-64.2,48.6
|
||||
c-25.9,10.8-56.1,30.6-78.7,43.5c-16.6,9.5-29.3,15.3-33.1,11.1C351.5,388.3,342.5,354.9,386.7,314z"/>
|
||||
<path class="st8" d="M773.8,272.5c33.4,12.4,58.6,33.3,75,50.7c11.4,12.1,18.4,22.5,20.9,27.2c6.1,11.4-12.7,14-36.2,5.3
|
||||
c-1-0.4-2.1-0.8-3.2-1.2c-25.3-9-84.3-25.6-101.1-33C705.1,310.9,717.2,251.5,773.8,272.5z"/>
|
||||
<path class="st5" d="M405.8,298.7c58-40,121.3-28.3,130.8-3.7c10.6,27.3-35.8,36.7-64.2,48.6c-25.9,10.8-56.1,30.6-78.7,43.5
|
||||
C370.3,352.9,386.3,320.8,405.8,298.7z"/>
|
||||
<path class="st5" d="M773.8,272.5c33.4,12.4,58.6,33.3,75,50.7c0.6,10.9-2.7,23.7-18.5,31.3c-25.3-9-84.3-25.6-101.1-33
|
||||
C705.1,310.9,717.2,251.5,773.8,272.5z"/>
|
||||
<path class="st5" d="M504.4,274.3c-72.3-19-136.4,35.7-149,74s-0.5,51.9,0,40.6s0-28.9,18-52.9c18.1-24,76-50.5,76-50.5
|
||||
S477.1,292.5,504.4,274.3z"/>
|
||||
<path class="st5" d="M869.5,341.4c-20.4-25.1-54.2-47.2-54.2-47.2s5.1,17,18.1,25.7c13,8.8,37.3,35.8,37.3,35.8
|
||||
S876.6,352.4,869.5,341.4z"/>
|
||||
<path class="st8" d="M504.4,294.2c-8.5,0.9-74.6,10.1-65.2-3.7s34.7-12.3,55.5-10.7C517.1,281.5,512.9,293.4,504.4,294.2z"/>
|
||||
<path class="st8" d="M803.6,314.5c-12.8-6.2-34.7-17.4-31.5-26s27.8,6.4,42.5,14C833.1,312,816.4,320.7,803.6,314.5z"/>
|
||||
<path class="st9" d="M445.2,436.6c75-42,139.1-61.4,202.6-61.1l0,0c15.2,0.1,30.5,1.3,45.8,3.6c15,2.3,30.1,5.6,45.5,9.9
|
||||
c66,18.5,208.6,88.2,186.5,282.7c-0.7,5.8-1.5,11.5-2.5,17.2C859.5,755.1,757.8,798,643,797.9c-139.3,0-259.6-63.2-315.5-154.7
|
||||
C334.6,558.1,380.2,476.8,445.2,436.6z"/>
|
||||
<path class="st5" d="M467,888.1c-4.1-2.1-15-8-19-10.1c-4.3-2.5-13.8-8.5-18.2-11.3c-5.7-3.9-11.5-8.7-17.3-12.6
|
||||
c-12.8-10.7-25.3-22-35.9-35c-51-59.2-67.4-143.5-50-219.2c14.9-66.8,52.5-130.4,110.4-168.2c74.4-43.3,162.1-73,249-60.2
|
||||
c66.6,9.1,131.6,37.9,178.2,87.3c47.5,48.6,68.8,117.8,66.7,185c-0.4,38.6-7.7,77.3-22.3,113.1C871.9,848.2,785.9,914.7,689,928
|
||||
C613.2,939.7,535.3,921.3,467,888.1L467,888.1z M467.7,886.6c103.1,51,224,52.6,322.3-10.2c62.5-40,109.6-104.2,126.4-177.1
|
||||
c12.6-54.7,12.2-114.2-9.7-166.5C878,462.3,811,414,739.3,394.7C633,365.6,539,391.2,446.1,446.3
|
||||
C361.9,503,321.5,614.9,336.4,714.1c5.5,34.6,19.1,68.2,39.4,96.7c3.2,4,7.6,9.9,10.8,13.9c6.6,7.1,14.9,16.4,22.4,22.6l5.2,4.8
|
||||
l5.6,4.3c6.3,5.3,16.2,11.9,23.2,16.3C449.7,877.2,460.6,882.6,467.7,886.6L467.7,886.6z"/>
|
||||
<path class="st10" d="M624.5,349.4c9.9-5,28,14.1,28,14.1C630,361.9,614.5,354.5,624.5,349.4z"/>
|
||||
<path class="st10" d="M709.7,347.4c-8.3-4.5-19.1,16.1-19.1,16.1C701.2,361.2,718,351.9,709.7,347.4z"/>
|
||||
<path class="st3" d="M145.4,160.9c33.1,40,68.6,87.5,94.4,130.5c16.5,27.5-104.9,230.6-126.9,268.9c-20.1,35.1-64.5-92-76.1-226
|
||||
C134.4,390.3,148.1,261.2,145.4,160.9z"/>
|
||||
<path class="st5" d="M239.3,291.7C203,231.8,156.6,179,109.9,127.3C94.3,110.6,78.5,93.1,61,78.7c-2.4-1.8-4.8-3.7-6.8-4.5
|
||||
c-2.9,2.5-5.2,9.1-6.4,13.8c-7.9,33.4-8.7,68.6-9.8,103.1c-0.4,23.2-0.5,46.6-0.3,69.9c-0.2,70,5.5,140.1,23.6,207.9
|
||||
c5.3,20,24.9,86.9,43.7,93.3c1.6-0.1,2.6-1.2,3.6-2.7l0.9-1.5c3.4-7.1,73.1-128.3,78.8-138.8C201.1,395.1,248.5,315.1,239.3,291.7
|
||||
L239.3,291.7z M240.3,291.1c10.1,18.9-35.6,108.8-46,131.3c-23,47.3-49.2,93.1-76.6,137.9c-2,4.1-7.1,10.6-13.3,9.8
|
||||
c-30.8-4.3-55.4-138.4-61-168.9C30.8,332,27.6,261.4,27.8,191.1c0.8-28.9-0.2-103.5,17.4-124.3c7.7-8.1,16.3-2.5,22.8,3
|
||||
c9.4,7.7,17.6,16.1,25.8,24.6C150,154,197.5,221.4,240.3,291.1L240.3,291.1z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.6 KiB |
14
src/shared/assets/coins/pyth.svg
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="CIRCLE_OUTLINE_BLACK" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
x="0px" y="0px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#E6DAFE;}
|
||||
</style>
|
||||
<circle class="st0" cx="256" cy="256" r="256"/>
|
||||
<path d="M303.4,228.5c0,20.7-16.7,37.5-37.4,37.5v37.5c41.3,0,74.7-33.5,74.7-74.9s-33.5-74.9-74.7-74.9c-13.6,0-26.4,3.6-37.4,10
|
||||
c-22.3,12.9-37.4,37.2-37.4,64.9v187.3l33.6,33.7l3.8,3.8V228.5c0-20.7,16.7-37.5,37.4-37.5S303.4,207.9,303.4,228.5z"/>
|
||||
<path d="M266,78.7c-27.2,0-52.7,7.3-74.7,20.1c-14.1,8.1-26.7,18.5-37.4,30.7c-23.2,26.4-37.4,61.1-37.4,99.1v112.4l37.4,37.5V228.5
|
||||
c0-33.3,14.4-63.2,37.4-83.8c10.8-9.7,23.4-17.3,37.4-22.2c11.7-4.2,24.3-6.4,37.4-6.4c61.9,0,112.1,50.3,112.1,112.4
|
||||
S327.9,340.9,266,340.9v37.5c82.5,0,149.4-67.1,149.4-149.8S348.5,78.7,266,78.7z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |